Files
nocobase/scripts/select-db-integration-matrix.mjs
T
chenos 69e212ce3f refactor: move commercial database drivers to Pro (#10865)
* refactor: move commercial database drivers to Pro

* fix: preserve automatic loading of commercial database drivers

* test: align OSS fixtures with optional Pro drivers
2026-10-15 09:34:54 +08:00

175 lines
5.3 KiB
JavaScript

import { execFileSync } from 'node:child_process';
import { appendFileSync } from 'node:fs';
export const databases = ['sqlite', 'postgres', 'mysql', 'oracle', 'mssql'];
// Keep whole packages covered, including their tests, manifests, and local configuration.
const sharedDirectories = [
'packages/libs/db/',
'packages/libs/db-testkit/',
'packages/libs/service-provider/',
'packages/libs/repository-input/',
'packages/tools/dev-config/',
'patches/',
];
const sharedFiles = new Set([
'package.json',
'pnpm-lock.yaml',
'pnpm-workspace.yaml',
'.npmrc',
'.pnpmfile.cjs',
'pnpmfile.cjs',
'scripts/select-db-integration-matrix.mjs',
'tests/scripts/select-db-integration-matrix.test.mjs',
]);
// Package tests whose subject is dialect-specific behavior, such as row locks, lock waits and indexed column changes. The
// matrix above runs the dialect packages' own suites; these run a package's named tests on that dialect through
// `pnpm test:db`, in the Database tests workflow, when a path they guard changes.
export const packageSuites = [
{
// Claims, consent decisions and the sweeper must not lock or write per queued candidate; see the test's header.
paths: [
'packages/plugins/app-plugin-agents/server/core/runs/',
'packages/plugins/app-plugin-agents/server/core/agents/agent.store.ts',
'packages/plugins/app-plugin-agents/tests/claim-concurrency.test.ts',
'packages/plugins/app-plugin-agents/tests/active-input.test.ts',
'packages/plugins/app-plugin-agents/database/migrations/202610260001_ag_run_revision.ts',
'packages/apps/studio/server/agents/work.ts',
'packages/plugins/app-plugin-agents/tests/harness.ts',
],
dialect: 'postgres',
filter: '@nocobase/app-plugin-agents',
tests: [
'tests/claim-concurrency.test.ts',
'tests/active-input.test.ts',
'tests/run-revision-migration.test.ts',
],
},
{
// SQL Server requires rebuilding the unique index before shrinking the legacy artifact-key column.
paths: [
'packages/plugins/app-plugin-releases/database/migrations/',
'packages/plugins/app-plugin-releases/tests/artifact-key-migration.test.ts',
],
dialect: 'mssql',
filter: '@nocobase/app-plugin-releases',
tests: ['tests/artifact-key-migration.test.ts'],
},
];
const suiteOf = ({ dialect, filter, tests }) => ({
dialect,
filter,
tests: tests.join(' '),
});
export function selectPackageSuites(changedPaths) {
return packageSuites
.filter((suite) =>
changedPaths.some((file) =>
suite.paths.some((guarded) =>
guarded.endsWith('/') ? file.startsWith(guarded) : file === guarded,
),
),
)
.map(suiteOf);
}
export function selectDatabases(changedPaths) {
const selected = new Set();
for (const file of changedPaths) {
if (
sharedFiles.has(file) ||
sharedDirectories.some((directory) => file.startsWith(directory))
) {
return [...databases];
}
for (const database of databases) {
if (file.startsWith(`packages/libs/db-${database}/`)) {
selected.add(database);
}
}
}
return databases.filter((database) => selected.has(database));
}
export function planDbIntegration({ baseSha, headSha, cwd = process.cwd() }) {
const git = (args) =>
execFileSync('git', args, {
cwd,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
maxBuffer: 16 * 1024 * 1024,
});
// A new branch, unavailable history, or an event without a base must never skip coverage.
if (!baseSha || /^0+$/.test(baseSha) || !headSha) {
return {
databases: [...databases],
suites: packageSuites.map(suiteOf),
reason: 'No comparison range; running every database.',
};
}
let base;
let head;
try {
base = git([
'rev-parse',
'--verify',
'--end-of-options',
`${baseSha}^{commit}`,
]).trim();
head = git([
'rev-parse',
'--verify',
'--end-of-options',
`${headSha}^{commit}`,
]).trim();
} catch {
return {
databases: [...databases],
suites: packageSuites.map(suiteOf),
reason: 'Comparison commits unavailable; running every database.',
};
}
// Treat renames as deletion + addition so moves into AND out of a package are covered.
// NUL delimiters preserve filenames containing whitespace, newlines, or non-ASCII characters.
// Do not catch diff errors: a broken selector must fail the planning job, never report a skip.
const changedPaths = git([
'diff',
'--name-only',
'--no-renames',
'-z',
base,
head,
'--',
])
.split('\0')
.filter(Boolean);
const selected = selectDatabases(changedPaths);
return {
databases: selected,
suites: selectPackageSuites(changedPaths),
reason: selected.length
? `Selected databases: ${selected.join(', ')}.`
: 'No database-related changes.',
};
}
if (import.meta.main) {
const plan = planDbIntegration({
baseSha: process.env.BASE_SHA,
headSha: process.env.HEAD_SHA,
});
const output = `databases=${JSON.stringify(plan.databases)}\nshould_run=${plan.databases.length > 0}\nsuites=${JSON.stringify(plan.suites)}\n`;
if (process.env.GITHUB_OUTPUT) {
appendFileSync(process.env.GITHUB_OUTPUT, output);
}
console.log(plan.reason);
process.stdout.write(output);
}