mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-04-28 06:19:46 +00:00
* fix(minimax): switch auth from x-api-key to Authorization Bearer (#1076) Integrated into release/v3.5.6 — MiniMax auth fix with authHeader consistency normalization * feat(CI,i18n): autogenerate language files + Add missing strings (#1071) Integrated into release/v3.5.6 — i18n translations for memory, skills, and missing keys across 31 languages * fix(ci): restore i18n continue-on-error, remove auto-commit race condition * fix(husky): load nvm in hooks for VS Code compatibility * fix(husky): gracefully skip hooks when npm is not in PATH * fix: convert OpenAI function tool_choice to Claude tool format (#1072) * fix: prevent EPIPE feedback loop filling logs at GB/s (#1006) * fix: fallback to native fetch when undici dispatcher fails (#1054) * fix: improve Qoder PAT validation with actionable error messages (#966) - Add QODER_PERSONAL_ACCESS_TOKEN env var fallback for both validation and execution - Pre-flight ping check to diagnose connectivity issues (Docker/proxy) - Detect encrypted auth blobs from ~/.qoder/.auth/user and guide to website PAT - Clear error messages for auth failures with link to integrations page - Treat non-auth 4xx as auth-pass (request format issue, not token issue) - Update tests to cover new validation paths (23 tests, all passing) * feat: Improve the Chinese translation (#1079) Integrated into release/v3.5.6 * chore(release): v3.5.6 — i18n updates and credential security fixes * fix(ci): resolve e2e and docs-sync pipeline failures * fix(security): bump next to 16.2.3 to resolve SNYK-JS-NEXT-15954202 * fix: guard Memory/Cache UI against null toLocaleString crash (#1083) * fix: translate OpenAI tool_choice type 'function' to Claude 'tool' format (#1072) * fix: pass custom baseUrl in provider API key validation (#1078) * docs: update CHANGELOG with v3.5.6 bug fixes and security patches * docs: rewrite implement-features workflow with 5-phase harvest-research-report-plan-execute pipeline * docs: organize _ideia/ into viable/defer/notfit + add Phase 2.5 auto-response workflow * docs: implementation plans for #1025, #750, #960, #1046 + close already-implemented #833, #973, #982 * feat: mask email addresses in dashboard for privacy (#1025) * feat: add OpenRouter and GitHub to embedding/image provider registries (#960) * feat: add model visibility toggle and search filter to provider page (#750) * docs: move implemented features to notfit, update task plans status * chore: untrack _ideia/ and _tasks/ from git — private/internal only * chore(release): bump to v3.5.6 — changelog, docs, version sync & any-budget fix * fix: remove explicit .ts extension in qoderCli import that caused 500 error in production build --------- Co-authored-by: Jean Brito <jeanfbrito@gmail.com> Co-authored-by: zenobit <zenobit@disroot.org> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: Ethan Hunt <136065060+only4copilot@users.noreply.github.com>
132 lines
3.8 KiB
JavaScript
132 lines
3.8 KiB
JavaScript
import { execFileSync } from "node:child_process";
|
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
|
|
const SOURCE_ROOTS = ["src/", "open-sse/", "electron/", "bin/"];
|
|
const TEST_PATTERNS = [/^tests\//, /(?:^|\/)__tests__\//, /\.(?:test|spec)\.[cm]?[jt]sx?$/];
|
|
// Test files for specific source types (e.g., Python validation scripts for i18n)
|
|
const TEST_FILE_PATTERNS = {
|
|
"src/i18n/messages/": [/\/scripts\/validate_translation\.py$/, /\/scripts\/check_translations\.py$/],
|
|
};
|
|
// Exclude directories that don't require tests (i18n has Python validation, docs, config)
|
|
const EXCLUDED_PATTERNS = [
|
|
/\/i18n\/messages\//, // i18n files have their own Python test scripts
|
|
/\.md$/, // Documentation
|
|
/\.yaml$/, // Config files
|
|
/\.yml$/, // Config files
|
|
];
|
|
|
|
function getArg(name, fallbackValue = "") {
|
|
const index = process.argv.indexOf(name);
|
|
if (index === -1 || index === process.argv.length - 1) {
|
|
return fallbackValue;
|
|
}
|
|
return process.argv[index + 1];
|
|
}
|
|
|
|
function runGit(args) {
|
|
return execFileSync("git", args, { encoding: "utf8" }).trim();
|
|
}
|
|
|
|
function isSourceFile(filePath) {
|
|
// Exclude patterns that don't require tests
|
|
if (EXCLUDED_PATTERNS.some(pattern => pattern.test(filePath))) {
|
|
return false;
|
|
}
|
|
return SOURCE_ROOTS.some((root) => filePath.startsWith(root));
|
|
}
|
|
|
|
function isTestFile(filePath) {
|
|
// Check standard test patterns
|
|
if (TEST_PATTERNS.some((pattern) => pattern.test(filePath))) {
|
|
return true;
|
|
}
|
|
// Check custom test file patterns for specific directories
|
|
for (const [dir, patterns] of Object.entries(TEST_FILE_PATTERNS)) {
|
|
if (filePath.startsWith(dir) && patterns.some((pattern) => pattern.test(filePath))) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function buildReport(lines) {
|
|
return `${lines.join("\n")}\n`;
|
|
}
|
|
|
|
const summaryFile = getArg("--summary-file", "");
|
|
const baseRef = process.env.GITHUB_BASE_REF;
|
|
|
|
if (!baseRef) {
|
|
const report = buildReport([
|
|
"## PR Test Policy",
|
|
"",
|
|
"Skipped: not running in a pull request context.",
|
|
]);
|
|
|
|
if (summaryFile) {
|
|
mkdirSync(path.dirname(summaryFile), { recursive: true });
|
|
writeFileSync(summaryFile, report);
|
|
}
|
|
|
|
process.stdout.write(report);
|
|
process.exit(0);
|
|
}
|
|
|
|
const baseTarget = process.env.GITHUB_BASE_SHA || `origin/${baseRef}`;
|
|
const changedFiles = runGit(["diff", "--name-only", "--diff-filter=ACMR", `${baseTarget}...HEAD`])
|
|
.split(/\r?\n/)
|
|
.map((line) => line.trim())
|
|
.filter(Boolean);
|
|
|
|
const changedSourceFiles = changedFiles.filter(isSourceFile);
|
|
const changedTestFiles = changedFiles.filter(isTestFile);
|
|
const hasRequiredTests = changedSourceFiles.length === 0 || changedTestFiles.length > 0;
|
|
|
|
const reportLines = [
|
|
"## PR Test Policy",
|
|
"",
|
|
`Base ref: \`${baseRef}\``,
|
|
`Changed production files: ${changedSourceFiles.length}`,
|
|
`Changed automated test files: ${changedTestFiles.length}`,
|
|
"",
|
|
];
|
|
|
|
if (changedSourceFiles.length > 0) {
|
|
reportLines.push("### Production files in scope", "");
|
|
for (const filePath of changedSourceFiles.slice(0, 20)) {
|
|
reportLines.push(`- \`${filePath}\``);
|
|
}
|
|
reportLines.push("");
|
|
}
|
|
|
|
if (changedTestFiles.length > 0) {
|
|
reportLines.push("### Tests in this PR", "");
|
|
for (const filePath of changedTestFiles.slice(0, 20)) {
|
|
reportLines.push(`- \`${filePath}\``);
|
|
}
|
|
reportLines.push("");
|
|
}
|
|
|
|
if (hasRequiredTests) {
|
|
reportLines.push("Result: PASS");
|
|
} else {
|
|
reportLines.push(
|
|
"Result: FAIL",
|
|
"",
|
|
"This PR changes production code under `src/`, `open-sse/`, `electron/`, or `bin/` but does not add or update automated tests."
|
|
);
|
|
}
|
|
|
|
const report = buildReport(reportLines);
|
|
|
|
if (summaryFile) {
|
|
mkdirSync(path.dirname(summaryFile), { recursive: true });
|
|
writeFileSync(summaryFile, report);
|
|
}
|
|
|
|
process.stdout.write(report);
|
|
|
|
if (!hasRequiredTests) {
|
|
process.exit(1);
|
|
}
|