mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 09:39:25 +00:00
improve(ci): shorten complete lint and typecheck jobs (#154150)
* ci: reuse typecheck state and bound lint programs * test: align compiler and code mode fixtures with current contracts
This commit is contained in:
parent
00ec0fbca4
commit
55ed23f278
13 changed files with 307 additions and 23 deletions
53
.github/workflows/ci.yml
vendored
53
.github/workflows/ci.yml
vendored
|
|
@ -3939,6 +3939,16 @@ jobs:
|
|||
install-bun: "false"
|
||||
dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && runner.environment == 'self-hosted' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }}
|
||||
|
||||
- name: Restore test-type incremental state
|
||||
id: test-type-cache
|
||||
if: matrix.task == 'test-types' && needs.preflight.outputs.cache_mode != 'off' && needs.preflight.outputs.frozen_target != 'true'
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: .artifacts/tsgo-cache
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-tsgo-v1-${{ matrix.task }}-${{ hashFiles('pnpm-lock.yaml', 'tsconfig*.json', 'test/tsconfig/*.json', 'scripts/run-tsgo*', 'scripts/lib/local-check-runtime.mts') }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-tsgo-v1-${{ matrix.task }}-${{ hashFiles('pnpm-lock.yaml', 'tsconfig*.json', 'test/tsconfig/*.json', 'scripts/run-tsgo*', 'scripts/lib/local-check-runtime.mts') }}-
|
||||
|
||||
- name: Compute extension boundary input fingerprint
|
||||
id: extension-boundary-inputs
|
||||
if: matrix.task == 'lint'
|
||||
|
|
@ -4279,6 +4289,14 @@ jobs:
|
|||
;;
|
||||
esac
|
||||
|
||||
- name: Save test-type incremental state
|
||||
if: success() && matrix.task == 'test-types' && needs.preflight.outputs.cache_write_allowed == 'true' && needs.preflight.outputs.cache_mode != 'off' && needs.preflight.outputs.frozen_target != 'true' && steps.test-type-cache.outputs.cache-hit != 'true'
|
||||
continue-on-error: true
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: .artifacts/tsgo-cache
|
||||
key: ${{ steps.test-type-cache.outputs.cache-primary-key }}
|
||||
|
||||
# Keep five core stripes; the runner isolates large targets into serial child processes.
|
||||
# Ordinary hybrid runs serialize the stripes across two hosted jobs.
|
||||
# GitHub, frozen and manual runs retain five jobs. All-Blacksmith keeps one lint job.
|
||||
|
|
@ -4371,7 +4389,7 @@ jobs:
|
|||
done
|
||||
|
||||
# Pair four original core stripes; check-test-types owns the fifth and its tail.
|
||||
# Each stripe retains fresh compiler processes and its two-child limit;
|
||||
# Each row retains fresh compiler processes and its two-child limit;
|
||||
# all-Blacksmith mode uses one 16-vCPU test-types job for the full workload.
|
||||
check-test-types-hosted-core-shard:
|
||||
permissions:
|
||||
|
|
@ -4396,7 +4414,19 @@ jobs:
|
|||
install-bun: "false"
|
||||
dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && runner.environment == 'self-hosted' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }}
|
||||
|
||||
- name: Restore core test-type incremental state
|
||||
id: test-type-cache
|
||||
if: needs.preflight.outputs.cache_mode != 'off' && needs.preflight.outputs.frozen_target != 'true'
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: .artifacts/tsgo-cache
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-tsgo-v1-core-${{ matrix.stripe }}-${{ hashFiles('pnpm-lock.yaml', 'tsconfig*.json', 'test/tsconfig/*.json', 'scripts/run-tsgo*', 'scripts/lib/local-check-runtime.mts') }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-tsgo-v1-core-${{ matrix.stripe }}-${{ hashFiles('pnpm-lock.yaml', 'tsconfig*.json', 'test/tsconfig/*.json', 'scripts/run-tsgo*', 'scripts/lib/local-check-runtime.mts') }}-
|
||||
|
||||
- name: Run hosted core test-types stripe
|
||||
env:
|
||||
FROZEN_TARGET: ${{ needs.preflight.outputs.frozen_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
|
@ -4406,9 +4436,24 @@ jobs:
|
|||
echo "[skip] target does not support core test-type stripes"
|
||||
exit 0
|
||||
fi
|
||||
for stripe in "$((2 * ${{ matrix.stripe }} - 1))" "$((2 * ${{ matrix.stripe }}))"; do
|
||||
node scripts/run-tsgo-core-test-shards.mjs --stripe "$stripe/5" --concurrency 2
|
||||
done
|
||||
first="$((2 * ${{ matrix.stripe }} - 1))"
|
||||
last="$((2 * ${{ matrix.stripe }}))"
|
||||
if [ "$FROZEN_TARGET" = "true" ]; then
|
||||
for stripe in "$first" "$last"; do
|
||||
node scripts/run-tsgo-core-test-shards.mjs --stripe "$stripe/5" --concurrency 2
|
||||
done
|
||||
else
|
||||
# One queue fills an idle slot across the former stripe barrier.
|
||||
node scripts/run-tsgo-core-test-shards.mjs --stripe "$first-$last/5" --concurrency 2
|
||||
fi
|
||||
|
||||
- name: Save core test-type incremental state
|
||||
if: success() && needs.preflight.outputs.cache_write_allowed == 'true' && needs.preflight.outputs.cache_mode != 'off' && needs.preflight.outputs.frozen_target != 'true' && steps.test-type-cache.outputs.cache-hit != 'true'
|
||||
continue-on-error: true
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: .artifacts/tsgo-cache
|
||||
key: ${{ steps.test-type-cache.outputs.cache-primary-key }}
|
||||
|
||||
check-additional-shard:
|
||||
permissions:
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ Docs-only `main` pushes skip CI and cache warming. The cache warmer publishes de
|
|||
|
||||
Core-test-only PRs use targeted type checks only when every selected test exists in the checkout. Deleting a core test keeps the full type-check plan, including the existing core stripes on GitHub and hybrid profiles.
|
||||
|
||||
Core lint includes `src/**/*.test-support.cjs` in type-aware checks through the bounded `src/tsconfig.json` discovery project. Other source files retain the root TypeScript project; unrelated JavaScript files are not added to this test-support project.
|
||||
Core lint discovers separate source and UI TypeScript projects, retaining shared ambient declarations and imported dependencies. The source project also includes `src/**/*.test-support.cjs`; unrelated JavaScript files are not added as roots. See [local checks](/ci/local-proof#local-equivalents).
|
||||
|
||||
Android native resource preparation uses the Mermaid renderer's filtered dependency install, including optional build tooling. Pnpm retains root dependencies but omits unrelated plugin packages; Gradle still builds the assets and runs the selected native tests and lint. Historical targets keep their compatibility path.
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,25 @@ local runs, Windows, explicit plugin stripes, and explicit serial selections kee
|
|||
eight-directory chunks. Explicit split-core and parallel execution selections
|
||||
remain unchanged.
|
||||
|
||||
Oxlint's type-aware backend discovers `src/tsconfig.json` and `ui/tsconfig.json`
|
||||
separately. Both inherit the root compiler options, include shared ambient
|
||||
declarations, and follow imported dependencies. Every existing lint target and
|
||||
rule still runs, including source CommonJS test preloads. The CLI `--tsconfig`
|
||||
option controls import resolution; it does not replace these discovery projects.
|
||||
|
||||
Current CI core-test rows combine their paired stripes into one queue of fresh
|
||||
compiler processes, with the same two-child limit. Each independent graph runs
|
||||
in incremental project mode: solution-build mode can miss an added root whose
|
||||
timestamp predates restored build information. Frozen targets keep their
|
||||
original stripe invocations. Per-graph elapsed times appear in the job log.
|
||||
|
||||
The test-type jobs restore their own `.artifacts/tsgo-cache` state across runs.
|
||||
Cache keys separate compiler/dependency/configuration versions and CI rows;
|
||||
the compiler still validates every selected graph after a hit. Pull requests
|
||||
only restore state, while the existing trusted cache writer policy controls
|
||||
publication after successful checks. Cache-off and frozen-target runs retain
|
||||
their original behavior. Lint programs do not share these compiler caches.
|
||||
|
||||
Oxlint keeps `eslint/no-redeclare` enabled for JavaScript. For `.ts`, `.tsx`,
|
||||
`.mts`, and `.cts`, `tsgo` owns declaration validity, including intentional
|
||||
type/value pairs with the same public name. `eslint/no-var` remains enabled
|
||||
|
|
|
|||
|
|
@ -147,16 +147,24 @@ export function selectTsgoCoreTestShards(
|
|||
export function selectTsgoCoreTestStripe(
|
||||
stripeSpec: string,
|
||||
): readonly { name: string; config: string }[] | undefined {
|
||||
const match = /^([1-9]\d*)\/([1-9]\d*)$/u.exec(stripeSpec);
|
||||
const match = /^([1-9]\d*)(?:-([1-9]\d*))?\/([1-9]\d*)$/u.exec(stripeSpec);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
const stripe = Number(match[1]);
|
||||
const stripeCount = Number(match[2]);
|
||||
if (stripe > stripeCount) {
|
||||
const lastStripe = Number(match[2] ?? match[1]);
|
||||
const stripeCount = Number(match[3]);
|
||||
if (
|
||||
![stripe, lastStripe, stripeCount].every(Number.isSafeInteger) ||
|
||||
stripe > lastStripe ||
|
||||
lastStripe > stripeCount
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
return TSGO_CORE_TEST_SHARDS.filter((_, index) => index % stripeCount === stripe - 1);
|
||||
return TSGO_CORE_TEST_SHARDS.filter((_, index) => {
|
||||
const owner = (index % stripeCount) + 1;
|
||||
return owner >= stripe && owner <= lastStripe;
|
||||
});
|
||||
}
|
||||
|
||||
export function findTsgoCoreTestShardViolations(params: {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ function runShard(config: string, env: NodeJS.ProcessEnv): Promise<number> {
|
|||
shell: false,
|
||||
args: distArtifactEntryArgs(
|
||||
path.join(repoRoot, "scripts/run-tsgo.mts"),
|
||||
["-b", config, "--builders", "1"],
|
||||
// These graphs have no project references. Project mode rechecks root
|
||||
// membership even when a restored build-info file is newer than a new root.
|
||||
["-p", config, "--incremental"],
|
||||
{ native: true },
|
||||
),
|
||||
cwd: repoRoot,
|
||||
|
|
@ -54,10 +56,14 @@ async function runTsgoCoreTestShards(
|
|||
if (!shard || failureCode !== 0) {
|
||||
return;
|
||||
}
|
||||
const startedAt = performance.now();
|
||||
const code = await runShard(shard.config, env).catch((error: unknown) => {
|
||||
failureCode = 1;
|
||||
throw error;
|
||||
});
|
||||
console.error(
|
||||
`[tsgo:${shard.name}] ${code === 0 ? "passed" : `failed (exit ${code})`} in ${((performance.now() - startedAt) / 1000).toFixed(1)}s`,
|
||||
);
|
||||
if (code !== 0 && failureCode === 0) {
|
||||
failureCode = code;
|
||||
}
|
||||
|
|
@ -142,7 +148,7 @@ if (isDirectRunUrl(process.argv[1], import.meta.url)) {
|
|||
const stripeSpec = process.argv[stripeFlagIndex + 1] ?? "";
|
||||
shards = selectTsgoCoreTestStripe(stripeSpec);
|
||||
if (!shards) {
|
||||
console.error(`Invalid core test stripe (expected i/n): ${stripeSpec}`);
|
||||
console.error(`Invalid core test stripe (expected i/n or first-last/n): ${stripeSpec}`);
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,21 @@
|
|||
"compilerOptions": {
|
||||
"allowJs": true
|
||||
},
|
||||
// Oxlint discovers this project for CommonJS test preloads; other sources use the root project.
|
||||
"include": ["**/*.test-support.cjs"]
|
||||
// Keep lint's source graph separate from UI and plugins, including shared ambient types.
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.mts",
|
||||
"**/*.cts",
|
||||
"**/*.test-support.cjs",
|
||||
"../packages/**/*.d.ts",
|
||||
"../packages/**/*.d.mts",
|
||||
"../packages/**/*.d.cts",
|
||||
"../ui/**/*.d.ts",
|
||||
"../ui/**/*.d.mts",
|
||||
"../ui/**/*.d.cts",
|
||||
"../extensions/**/*.d.ts",
|
||||
"../extensions/**/*.d.mts",
|
||||
"../extensions/**/*.d.cts"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@ function evaluateWorkflowExpression(
|
|||
);
|
||||
return runInNewContext(evaluableSource, {
|
||||
always: () => true,
|
||||
success: () => !context.failed && !context.cancelled,
|
||||
failure: () => context.failed ?? false,
|
||||
cancelled: () => context.cancelled ?? false,
|
||||
// GitHub expression builtins the runner-routing clauses use.
|
||||
|
|
@ -2191,7 +2192,8 @@ function runCheckShardFixture(options: {
|
|||
`import { appendFileSync } from "node:fs";
|
||||
const args = process.argv.slice(2);
|
||||
appendFileSync(process.env.TYPE_CALLS, [process.env.TYPE_ROW, process.env.OPENCLAW_LOCAL_CHECK ?? "<unset>", "node " + args.join(" ")].join("\\t") + "\\n");
|
||||
if (args[args.indexOf("--stripe") + 1] === process.env.FAIL_TYPE_STRIPE) process.exit(17);
|
||||
const stripe = args[args.indexOf("--stripe") + 1];
|
||||
if (stripe === process.env.FAIL_TYPE_STRIPE || stripe?.replace(/-\\d+\\//, "/") === process.env.FAIL_TYPE_STRIPE) process.exit(17);
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
|
@ -12948,6 +12950,61 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
|
|||
}
|
||||
});
|
||||
|
||||
it("reuses isolated test-type caches without bypassing compilation or cache authority", () => {
|
||||
const workflow = readCiWorkflow();
|
||||
for (const [jobId, runName] of [
|
||||
["check-shard", "Run check shard"],
|
||||
["check-test-types-hosted-core-shard", "Run hosted core test-types stripe"],
|
||||
] as const) {
|
||||
const steps = workflow.jobs[jobId].steps as WorkflowStep[];
|
||||
const restore = expectDefined(
|
||||
steps.find((step) => step.id === "test-type-cache"),
|
||||
`${jobId} compiler cache`,
|
||||
);
|
||||
const save = expectDefined(
|
||||
steps.find(
|
||||
(step) => step.name?.startsWith("Save") && step.with?.path === ".artifacts/tsgo-cache",
|
||||
),
|
||||
`${jobId} compiler cache writer`,
|
||||
);
|
||||
const run = expectDefined(
|
||||
steps.find((step) => step.name === runName),
|
||||
`${jobId} compiler`,
|
||||
);
|
||||
expect(steps.indexOf(restore)).toBeLessThan(steps.indexOf(run));
|
||||
expect(steps.indexOf(save)).toBeGreaterThan(steps.indexOf(run));
|
||||
expect(run.if).toBeUndefined();
|
||||
expect(run.run).not.toContain("cache-hit");
|
||||
expect(restore.with?.path).toBe(".artifacts/tsgo-cache");
|
||||
expect(restore.with?.key).toContain("pnpm-lock.yaml");
|
||||
expect(restore.with?.key).toContain("test/tsconfig/*.json");
|
||||
expect(restore.with?.key).toContain(
|
||||
jobId === "check-shard" ? "matrix.task" : "matrix.stripe",
|
||||
);
|
||||
expect(save.with?.key).toBe("${{ steps.test-type-cache.outputs.cache-primary-key }}");
|
||||
for (const [cacheMode, writable, frozen, failed, canRestore, canSave] of [
|
||||
["restore", false, false, false, true, false],
|
||||
["restore", true, false, false, true, true],
|
||||
["off", true, false, false, false, false],
|
||||
["restore", true, true, false, false, false],
|
||||
["restore", true, false, true, true, false],
|
||||
] as const) {
|
||||
const context: Parameters<typeof evaluateWorkflowExpression>[1] = {
|
||||
eventName: writable ? "push" : "pull_request",
|
||||
repository: "openclaw/openclaw",
|
||||
runAttempt: 1,
|
||||
frozenTarget: frozen,
|
||||
failed,
|
||||
matrix: { task: "test-types", stripe: 1 },
|
||||
preflightOutputs: { cache_mode: cacheMode, cache_write_allowed: String(writable) },
|
||||
steps: { "test-type-cache": { outputs: { "cache-hit": "false" } } },
|
||||
};
|
||||
expect(evaluateWorkflowExpression("${{ " + restore.if + " }}", context)).toBe(canRestore);
|
||||
expect(evaluateWorkflowExpression("${{ " + save.if + " }}", context)).toBe(canSave);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
["hybrid", "pull_request", false, true, true, true],
|
||||
["github", "workflow_dispatch", false, true, true, true],
|
||||
|
|
@ -12976,7 +13033,11 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
|
|||
.filter((call) => call.row === row.name)
|
||||
.map((call) => call.command.split(" ")[2]),
|
||||
),
|
||||
).toEqual([["1/5", "2/5"], ["3/5", "4/5"], ["5/5"]]);
|
||||
).toEqual(
|
||||
frozenTarget
|
||||
? [["1/5", "2/5"], ["3/5", "4/5"], ["5/5"]]
|
||||
: [["1-2/5"], ["3-4/5"], ["5/5"]],
|
||||
);
|
||||
for (const call of stripes) {
|
||||
const args = call.command.split(" ").slice(1);
|
||||
expect(args).toEqual(["--stripe", expect.any(String), "--concurrency", "2"]);
|
||||
|
|
@ -13006,7 +13067,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
|
|||
expect(result.rows.filter((row) => row.status === 0)).toHaveLength(2);
|
||||
expect(
|
||||
result.typeCalls.filter((call) => call.row === failed[0]!.name).map((call) => call.command),
|
||||
).toEqual([`node --stripe ${failStripe} --concurrency 2`]);
|
||||
).toEqual([`node --stripe ${failStripe === "1/5" ? "1-2/5" : failStripe} --concurrency 2`]);
|
||||
});
|
||||
|
||||
it.each(["main", "trunk/release"])(
|
||||
|
|
|
|||
|
|
@ -372,7 +372,7 @@ describe("native check launchers in paths with spaces", () => {
|
|||
);
|
||||
expect(child.argv).toEqual(
|
||||
compiler
|
||||
? ["-b", TSGO_CORE_TEST_SHARDS[0].config, "--builders", "1"]
|
||||
? ["-p", TSGO_CORE_TEST_SHARDS[0].config, "--incremental"]
|
||||
: ["--mode=package-boundary"],
|
||||
);
|
||||
const lock = resolveDistArtifactLockPath(root);
|
||||
|
|
|
|||
|
|
@ -939,8 +939,9 @@ describe("mock OpenAI response markers", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("resumes the MCP Code Mode fixture until the latest result completes", async () => {
|
||||
await withMockServer(mockOpenAiPath, {}, async (baseUrl) => {
|
||||
it.each(["current", "legacy"])("resumes the MCP Code Mode fixture (%s catalog)", async (mode) => {
|
||||
const env = { OPENCLAW_FROZEN_TARGET_MCP_CODE_MODE_CATALOG_MODE: mode };
|
||||
await withMockServer(mockOpenAiPath, env, async (baseUrl) => {
|
||||
const input: Record<string, unknown>[] = [
|
||||
{ content: "mcp code mode api file qa check", role: "user" },
|
||||
];
|
||||
|
|
@ -963,9 +964,13 @@ describe("mock OpenAI response markers", () => {
|
|||
};
|
||||
const first = await request();
|
||||
expect(first.output?.[0]).toMatchObject({ name: "exec", type: "function_call" });
|
||||
expect(JSON.parse(first.output[0].arguments)).toEqual({
|
||||
const execArguments = JSON.parse(first.output[0].arguments);
|
||||
expect(execArguments).toEqual({
|
||||
code: expect.stringContaining('MCP.fixture.lookupNote({ id: "alpha" })'),
|
||||
});
|
||||
expect(execArguments.code).toContain(
|
||||
mode === "legacy" ? "ALL_TOOLS.some(" : "catalog.all().some(",
|
||||
);
|
||||
|
||||
for (const reason of ["pending_tools", "yield"]) {
|
||||
input.push({
|
||||
|
|
|
|||
|
|
@ -531,7 +531,6 @@ describe("MCP code-mode gateway Docker client result validation", () => {
|
|||
id: "mcp-code-mode-exec",
|
||||
name: "exec",
|
||||
arguments: {
|
||||
language: "javascript",
|
||||
code: 'return await MCP.fixture.lookupNote({ id: "alpha" });',
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -362,6 +362,99 @@ describe("oxlint config", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("keeps source and UI lint projects bounded with imported and ambient types", () => {
|
||||
const tempRoot = fs.realpathSync(createTempDir("openclaw-oxlint-core-projects-"));
|
||||
for (const file of [
|
||||
".oxlintrc.json",
|
||||
"tsconfig.json",
|
||||
"src/tsconfig.json",
|
||||
"ui/tsconfig.json",
|
||||
]) {
|
||||
if (fs.existsSync(file)) {
|
||||
const target = path.join(tempRoot, file);
|
||||
fs.mkdirSync(path.dirname(target), { recursive: true });
|
||||
fs.copyFileSync(file, target);
|
||||
}
|
||||
}
|
||||
fs.symlinkSync(path.resolve("node_modules"), path.join(tempRoot, "node_modules"), "junction");
|
||||
const source = [
|
||||
'import { work } from "../packages/imported.js";',
|
||||
"work(); fromCore(); fromUi(); fromPackage(); fromPlugin(); fromMts(); fromCts();",
|
||||
].join("\n");
|
||||
for (const [file, content] of Object.entries({
|
||||
"src/owner.ts": source,
|
||||
"ui/owner.ts": source,
|
||||
"packages/imported.ts": "export function work(): Promise<void> { return Promise.resolve(); }",
|
||||
"src/contracts.d.ts": "declare function fromCore(): Promise<void>;",
|
||||
"ui/contracts.d.ts": "declare function fromUi(): Promise<void>;",
|
||||
"packages/contracts.d.ts": "declare function fromPackage(): Promise<void>;",
|
||||
"extensions/contracts.d.ts": "declare function fromPlugin(): Promise<void>;",
|
||||
"packages/contracts.d.mts":
|
||||
"export {}; declare global { function fromMts(): Promise<void>; }",
|
||||
"packages/contracts.d.cts":
|
||||
"export {}; declare global { function fromCts(): Promise<void>; }",
|
||||
})) {
|
||||
const target = path.join(tempRoot, file);
|
||||
fs.mkdirSync(path.dirname(target), { recursive: true });
|
||||
fs.writeFileSync(target, content);
|
||||
}
|
||||
const selected = ["src/owner.ts", "ui/owner.ts"];
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[
|
||||
path.resolve("node_modules/oxlint/bin/oxlint"),
|
||||
"--type-aware",
|
||||
"--format",
|
||||
"json",
|
||||
"--threads=1",
|
||||
...selected,
|
||||
],
|
||||
{
|
||||
cwd: tempRoot,
|
||||
encoding: "utf8",
|
||||
timeout: 10_000,
|
||||
env: {
|
||||
...process.env,
|
||||
OXC_LOG: "debug",
|
||||
GOMAXPROCS: "2",
|
||||
OXLINT_TSGOLINT_PATH: path.resolve(
|
||||
"node_modules/.bin",
|
||||
process.platform === "win32" ? "tsgolint.CMD" : "tsgolint",
|
||||
),
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(result.error).toBeUndefined();
|
||||
expect(result.status, result.stderr).toBe(1);
|
||||
const report = JSON.parse(result.stdout) as {
|
||||
diagnostics: Array<{ filename: string; code: string }>;
|
||||
};
|
||||
for (const file of selected) {
|
||||
expect(
|
||||
report.diagnostics
|
||||
.filter((diagnostic) => diagnostic.filename.replaceAll("\\", "/") === file)
|
||||
.map((diagnostic) => diagnostic.code),
|
||||
).toEqual(Array.from({ length: 7 }, () => "typescript(no-floating-promises)"));
|
||||
const owner = path.dirname(file);
|
||||
expect(result.stderr.replaceAll("\\", "/")).toContain(
|
||||
`Got tsconfig for file ${path.join(tempRoot, file).replaceAll("\\", "/")}: ${path.join(tempRoot, owner, "tsconfig.json").replaceAll("\\", "/")}`,
|
||||
);
|
||||
const project = spawnSync(
|
||||
process.execPath,
|
||||
[
|
||||
path.resolve("node_modules/typescript-native/bin/tsc"),
|
||||
"--showConfig",
|
||||
"-p",
|
||||
`${owner}/tsconfig.json`,
|
||||
],
|
||||
{ cwd: tempRoot, encoding: "utf8", timeout: 10_000 },
|
||||
);
|
||||
expect(project.status, project.stdout + project.stderr).toBe(0);
|
||||
const parsed = JSON.parse(project.stdout) as { files: string[] };
|
||||
expect(parsed.files).not.toContain(`../${owner === "src" ? "ui" : "src"}/owner.ts`);
|
||||
}
|
||||
});
|
||||
|
||||
it("checks unbound methods in TypeScript and CommonJS source test support", () => {
|
||||
const tempRoot = fs.realpathSync(createTempDir("openclaw-oxlint-source-support-"));
|
||||
for (const file of [".oxlintrc.json", "tsconfig.json", "src/tsconfig.json"]) {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ describe("tsgo core test shards", () => {
|
|||
throw new Error(`Could not parse ${config}`);
|
||||
}
|
||||
expect(parsed.errors, config).toEqual([]);
|
||||
expect(parsed.projectReferences ?? [], config).toEqual([]);
|
||||
return parsed.fileNames
|
||||
.filter((file) => /\.test\.tsx?$/u.test(file))
|
||||
.map((file) => path.relative(process.cwd(), file).replaceAll(path.sep, "/"));
|
||||
|
|
@ -108,6 +109,19 @@ describe("tsgo core test shards", () => {
|
|||
expect(selectTsgoCoreTestStripe("0/2")).toBeUndefined();
|
||||
expect(selectTsgoCoreTestStripe("3/2")).toBeUndefined();
|
||||
expect(selectTsgoCoreTestStripe("src")).toBeUndefined();
|
||||
expect(selectTsgoCoreTestStripe("2-1/5")).toBeUndefined();
|
||||
expect(selectTsgoCoreTestStripe("1-6/5")).toBeUndefined();
|
||||
const paired = ["1-2/5", "3-4/5", "5/5"].flatMap(
|
||||
(stripe) => selectTsgoCoreTestStripe(stripe) ?? [],
|
||||
);
|
||||
expect(paired.map((shard) => shard.name).toSorted()).toEqual(
|
||||
TSGO_CORE_TEST_SHARDS.map((shard) => shard.name).toSorted(),
|
||||
);
|
||||
expect(selectTsgoCoreTestStripe("1-2/5")).toEqual(
|
||||
TSGO_CORE_TEST_SHARDS.filter((shard) =>
|
||||
["1/5", "2/5"].some((stripe) => selectTsgoCoreTestStripe(stripe)?.includes(shard)),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("accepts an exact once-only partition within the root budget", () => {
|
||||
|
|
@ -398,9 +412,10 @@ process.exit(result.status??1);
|
|||
expect(calls.filter((args) => args.includes("--listFilesOnly"))).toHaveLength(
|
||||
TSGO_CORE_GRAPHS.length,
|
||||
);
|
||||
// Discovery and diagnostic checks both use project mode.
|
||||
const builds = calls
|
||||
.filter((args) => args.includes("-b"))
|
||||
.map((args) => args[args.indexOf("-b") + 1]);
|
||||
.filter((args) => !args.includes("--listFilesOnly") && !args.includes("--showConfig"))
|
||||
.map((args) => args[args.indexOf("-p") + 1]);
|
||||
return { result, builds };
|
||||
};
|
||||
const initial = await check();
|
||||
|
|
|
|||
18
ui/tsconfig.json
Normal file
18
ui/tsconfig.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.mts",
|
||||
"**/*.cts",
|
||||
"../src/**/*.d.ts",
|
||||
"../src/**/*.d.mts",
|
||||
"../src/**/*.d.cts",
|
||||
"../packages/**/*.d.ts",
|
||||
"../packages/**/*.d.mts",
|
||||
"../packages/**/*.d.cts",
|
||||
"../extensions/**/*.d.ts",
|
||||
"../extensions/**/*.d.mts",
|
||||
"../extensions/**/*.d.cts"
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue