mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(ci): keep unused exports advisory (#105704)
* fix(ci): preserve frozen deadcode contracts * fix(ci): keep unused exports advisory
This commit is contained in:
@@ -1597,12 +1597,21 @@ jobs:
|
||||
fi
|
||||
;;
|
||||
dependencies)
|
||||
if pnpm run --silent 2>/dev/null | grep -q '^ deadcode:dependencies$'; then
|
||||
has_package_script() {
|
||||
node -e '
|
||||
const scripts = require("./package.json").scripts ?? {};
|
||||
process.exit(Object.hasOwn(scripts, process.argv[1]) ? 0 : 1);
|
||||
' "$1"
|
||||
}
|
||||
if has_package_script "deadcode:dependencies" &&
|
||||
has_package_script "deadcode:unused-files"; then
|
||||
pnpm deadcode:dependencies
|
||||
pnpm deadcode:unused-files
|
||||
pnpm deadcode:exports
|
||||
else
|
||||
elif [[ "$HISTORICAL_TARGET" == "true" ]] && has_package_script "deadcode:ci"; then
|
||||
pnpm deadcode:ci
|
||||
else
|
||||
echo "Target does not provide a supported deadcode check." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
test-types)
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ job budget.
|
||||
|
||||
Android CI runs both `testPlayDebugUnitTest` and `testThirdPartyDebugUnitTest` and then builds the Play debug APK. The third-party flavor has no separate source set or manifest; its unit-test lane still compiles the flavor with the SMS/call-log BuildConfig flags, while avoiding a duplicate debug APK packaging job on every Android-relevant push.
|
||||
|
||||
The `check-dependencies` shard runs a production Knip dependency-only pass, the unused-file allowlist check, and the enforced unused-exports baseline ratchet. The unused-file guard fails when a PR adds a new unreviewed unused file or leaves a stale allowlist entry, while preserving intentional dynamic plugin, generated, build, live-test, and package bridge surfaces that Knip cannot resolve statically. The unused-exports ratchet likewise rejects new findings and stale required baseline entries, so the checked-in debt can only shrink. The former ts-prune and ts-unused-exports advisory reports no longer run.
|
||||
The `check-dependencies` shard runs a production Knip dependency-only pass and the unused-file allowlist check. The unused-file guard fails when a PR adds a new unreviewed unused file or leaves a stale allowlist entry, while preserving intentional dynamic plugin, generated, build, live-test, and package bridge surfaces that Knip cannot resolve statically. The unused-exports baseline remains available as an advisory maintenance scan through `pnpm deadcode:exports`; after deleting dead code, regenerate it with `pnpm deadcode:exports:update`.
|
||||
|
||||
## ClawSweeper activity forwarding
|
||||
|
||||
|
||||
@@ -385,6 +385,55 @@ function writeExecutable(filePath: string, lines: string[]): void {
|
||||
chmodSync(filePath, 0o755);
|
||||
}
|
||||
|
||||
function runDependencyCheckFixture(options: { historicalTarget: boolean; scripts: string[] }): {
|
||||
calls: string[];
|
||||
output: string;
|
||||
status: number | null;
|
||||
} {
|
||||
const root = mkdtempSync(path.join(tmpdir(), "openclaw-ci-deadcode-"));
|
||||
try {
|
||||
const fakeBin = path.join(root, "bin");
|
||||
const callsPath = path.join(root, "pnpm-calls.txt");
|
||||
mkdirSync(fakeBin);
|
||||
writeFileSync(
|
||||
path.join(root, "package.json"),
|
||||
`${JSON.stringify({
|
||||
scripts: Object.fromEntries(options.scripts.map((name) => [name, "true"])),
|
||||
})}\n`,
|
||||
);
|
||||
writeExecutable(path.join(fakeBin, "pnpm"), [
|
||||
"#!/usr/bin/env bash",
|
||||
"set -euo pipefail",
|
||||
'printf "%s\\n" "$*" >> "$PNPM_CALLS"',
|
||||
]);
|
||||
const checkShardRun = readCiWorkflow().jobs["check-shard"].steps.find(
|
||||
(step: WorkflowStep) => step.name === "Run check shard",
|
||||
).run;
|
||||
const run = spawnSync("bash", ["-c", checkShardRun], {
|
||||
cwd: root,
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...process.env,
|
||||
FORMAT_CHECK: "false",
|
||||
HISTORICAL_TARGET: options.historicalTarget ? "true" : "false",
|
||||
PATH: `${fakeBin}:${process.env.PATH ?? ""}`,
|
||||
PNPM_CALLS: callsPath,
|
||||
PR_BASE_SHA: "",
|
||||
TASK: "dependencies",
|
||||
},
|
||||
});
|
||||
return {
|
||||
calls: existsSync(callsPath)
|
||||
? readFileSync(callsPath, "utf8").trim().split("\n").filter(Boolean)
|
||||
: [],
|
||||
output: `${run.stdout}${run.stderr}`,
|
||||
status: run.status,
|
||||
};
|
||||
} finally {
|
||||
rmSync(root, { force: true, recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
function runGeneratedPublisherScenario(
|
||||
baseChangePath: "a" | "b" | null,
|
||||
options: {
|
||||
@@ -1697,6 +1746,44 @@ describe("ci workflow guards", () => {
|
||||
expect(preflightGuards).toContain("pnpm deps:patches:check");
|
||||
});
|
||||
|
||||
it("uses stable deadcode checks for current and frozen checkouts", () => {
|
||||
const modern = runDependencyCheckFixture({
|
||||
historicalTarget: false,
|
||||
scripts: ["deadcode:dependencies", "deadcode:unused-files", "deadcode:exports"],
|
||||
});
|
||||
expect(modern.status, modern.output).toBe(0);
|
||||
expect(modern.calls).toEqual(["deadcode:dependencies", "deadcode:unused-files"]);
|
||||
|
||||
const frozen = runDependencyCheckFixture({
|
||||
historicalTarget: true,
|
||||
scripts: [
|
||||
"deadcode:ci",
|
||||
"deadcode:dependencies",
|
||||
"deadcode:report:ci:ts-unused",
|
||||
"deadcode:unused-files",
|
||||
],
|
||||
});
|
||||
expect(frozen.status, frozen.output).toBe(0);
|
||||
expect(frozen.calls).toEqual(["deadcode:dependencies", "deadcode:unused-files"]);
|
||||
|
||||
const legacy = runDependencyCheckFixture({
|
||||
historicalTarget: true,
|
||||
scripts: ["deadcode:ci"],
|
||||
});
|
||||
expect(legacy.status, legacy.output).toBe(0);
|
||||
expect(legacy.calls).toEqual(["deadcode:ci"]);
|
||||
|
||||
const incompleteCurrent = runDependencyCheckFixture({
|
||||
historicalTarget: false,
|
||||
scripts: ["deadcode:dependencies"],
|
||||
});
|
||||
expect(incompleteCurrent.status).toBe(1);
|
||||
expect(incompleteCurrent.calls).toEqual([]);
|
||||
expect(incompleteCurrent.output).toContain(
|
||||
"Target does not provide a supported deadcode check.",
|
||||
);
|
||||
});
|
||||
|
||||
it("runs mobile protocol coverage for Node and native-only changes", () => {
|
||||
const workflow = readCiWorkflow();
|
||||
const coverageStep = workflow.jobs.preflight.steps.find(
|
||||
@@ -1978,6 +2065,13 @@ describe("ci workflow guards", () => {
|
||||
);
|
||||
expect(checkShard.run).toContain("pnpm tsgo:scripts");
|
||||
expect(checkShard.run).toContain('elif [[ "$HISTORICAL_TARGET" != "true" ]]');
|
||||
expect(checkShard.run).toContain('has_package_script "deadcode:dependencies"');
|
||||
expect(checkShard.run).toContain('has_package_script "deadcode:unused-files"');
|
||||
expect(checkShard.run).not.toContain('has_package_script "deadcode:exports"');
|
||||
expect(checkShard.run).toContain(
|
||||
'elif [[ "$HISTORICAL_TARGET" == "true" ]] && has_package_script "deadcode:ci"',
|
||||
);
|
||||
expect(checkShard.run).toContain("Target does not provide a supported deadcode check.");
|
||||
|
||||
const uiInstall = workflow.jobs["checks-ui"].steps.find(
|
||||
(step: { name?: string }) => step.name === "Install Playwright Chromium",
|
||||
|
||||
Reference in New Issue
Block a user