mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 13:31:38 +00:00
* feat(ci): weekly Codex sweep for date-carrying to-dos Mondays 06:23 UTC (plus default-branch-only manual dispatch with dry_run): a deterministic prefilter collects candidate lines where to-do markers co-occur with date tokens, plus every deprecated compat record from the plugin registry; a pinned openai/codex-action step judges each candidate in context (genuine dated commitment vs historical date or fixture) and writes an OVERDUE / DUE-30-days / FUTURE report; a separate privileged job validates the report (tracked file:line locations, date shape, inert text) and upserts one marker-tagged tracking issue via the Barnacle app token, commenting only when items newly become due. The app token is minted on a fresh runner and checkout — never beside Codex or its child processes; only the validated report artifact crosses jobs. No permission-* subsets on token minting (installations reject explicit subsets; see pr-ci-sweeper). * chore(ci): localize upsert helpers with no external consumers
135 lines
5.4 KiB
TypeScript
135 lines
5.4 KiB
TypeScript
import { spawnSync } from "node:child_process";
|
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { cleanupTempDirs, makeTempDir } from "../helpers/temp-dir.js";
|
|
|
|
const scriptPath = path.resolve("scripts/dated-todo-scan.mjs");
|
|
const tempDirs: string[] = [];
|
|
|
|
afterEach(() => {
|
|
cleanupTempDirs(tempDirs);
|
|
});
|
|
|
|
function writeFixture(root: string, file: string, contents: string) {
|
|
const target = path.join(root, file);
|
|
mkdirSync(path.dirname(target), { recursive: true });
|
|
writeFileSync(target, contents);
|
|
}
|
|
|
|
describe("dated-todo-scan", () => {
|
|
it("finds nearby dated commitments, ignores noise, and includes deprecated compat records", () => {
|
|
const root = makeTempDir(tempDirs, "dated-todo-scan-");
|
|
const reportRoot = makeTempDir(tempDirs, "dated-todo-report-");
|
|
writeFixture(root, "src/direct.ts", "// TODO: remove this after 2026-10-01\n");
|
|
writeFixture(root, "src/expiry.ts", "// Compatibility expiry: March 1 2027.\n");
|
|
writeFixture(root, "src/expiration.ts", "// Temporary expiration: March 1, 2027.\n");
|
|
writeFixture(root, "src/no-comma-space.ts", "// TODO remove after March 1,2027.\n");
|
|
writeFixture(root, "src/timestamp.ts", "// Temporary expiry 2026-10-01T00:00:00Z.\n");
|
|
writeFixture(root, "config/outside.ts", "// TODO remove after 2027-06-01.\n");
|
|
writeFixture(root, "config/ignored.ts", "// TODO remove after 2027-06-02.\n");
|
|
writeFixture(root, ".gitignore", "config/ignored.ts\n");
|
|
writeFixture(
|
|
root,
|
|
"docs/adjacent.md",
|
|
"<!-- temporary workaround -->\nDeadline: March 2027.\n",
|
|
);
|
|
writeFixture(
|
|
root,
|
|
"test/removal.test.ts",
|
|
"const removalDatePendingCompatCodes = [\n // keep until July 2028\n];\n",
|
|
);
|
|
writeFixture(root, "src/history.ts", "// Released on 2026-01-01.\n");
|
|
writeFixture(root, "src/no-date.ts", "// TODO: improve this eventually.\n");
|
|
writeFixture(
|
|
root,
|
|
"src/too-far.ts",
|
|
"// FIXME: remove this\nconst gap = true;\n// 2027-04-01\n",
|
|
);
|
|
writeFixture(root, "CHANGELOG.md", "TODO remove after 2026-09-01\n");
|
|
writeFixture(root, "test/fixtures/noise.ts", "// TODO remove after 2026-09-01\n");
|
|
writeFixture(root, "docs/.generated/noise.md", "Temporary until 2026-09-01\n");
|
|
writeFixture(root, "locales/en.json", '{"todo":"remove after 2026-09-01"}\n');
|
|
writeFixture(
|
|
root,
|
|
"src/plugins/compat/registry.ts",
|
|
[
|
|
'const prefix = { code: "fixture-deprecation-extra" };',
|
|
'const record = { code: "fixture-deprecation" };',
|
|
"",
|
|
].join("\n"),
|
|
);
|
|
spawnSync("git", ["init", "-q"], { cwd: root });
|
|
spawnSync("git", ["add", "."], { cwd: root });
|
|
spawnSync("git", ["add", "-f", "config/ignored.ts"], { cwd: root });
|
|
writeFixture(root, "src/untracked.ts", "// TODO remove after 2026-12-01\n");
|
|
|
|
const compatReport = path.join(reportRoot, "compat.json");
|
|
writeFileSync(
|
|
compatReport,
|
|
JSON.stringify({
|
|
compat: {
|
|
records: [
|
|
{
|
|
code: "fixture-deprecation",
|
|
status: "deprecated",
|
|
removeAfter: "2026-11-15",
|
|
replacement: "fixture replacement",
|
|
},
|
|
{ code: "active-record", status: "active", removeAfter: "2026-12-01" },
|
|
{ code: "undated-deprecation", status: "deprecated" },
|
|
],
|
|
},
|
|
}),
|
|
);
|
|
const output = path.join(root, ".artifacts", "candidates.json");
|
|
const result = spawnSync(
|
|
process.execPath,
|
|
[scriptPath, "--root", root, "--output", output, "--compat-report", compatReport],
|
|
{ encoding: "utf8" },
|
|
);
|
|
|
|
expect(result.status, result.stderr).toBe(0);
|
|
const candidates = JSON.parse(readFileSync(output, "utf8")) as Array<{
|
|
file: string;
|
|
line: number;
|
|
text: string;
|
|
source: string;
|
|
}>;
|
|
expect(candidates).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ file: "src/direct.ts", line: 1, source: "scan" }),
|
|
expect.objectContaining({ file: "src/expiration.ts", line: 1, source: "scan" }),
|
|
expect.objectContaining({ file: "src/expiry.ts", line: 1, source: "scan" }),
|
|
expect.objectContaining({ file: "src/no-comma-space.ts", line: 1, source: "scan" }),
|
|
expect.objectContaining({ file: "src/timestamp.ts", line: 1, source: "scan" }),
|
|
expect.objectContaining({ file: "config/outside.ts", line: 1, source: "scan" }),
|
|
expect.objectContaining({ file: "config/ignored.ts", line: 1, source: "scan" }),
|
|
expect.objectContaining({ file: "docs/adjacent.md", line: 1, source: "scan" }),
|
|
expect.objectContaining({ file: "test/removal.test.ts", line: 2, source: "scan" }),
|
|
expect.objectContaining({
|
|
file: "src/plugins/compat/registry.ts",
|
|
line: 2,
|
|
source: "compat-registry",
|
|
text: expect.stringContaining("fixture-deprecation: removeAfter 2026-11-15"),
|
|
}),
|
|
]),
|
|
);
|
|
expect(candidates.map((candidate) => candidate.file)).not.toEqual(
|
|
expect.arrayContaining([
|
|
"CHANGELOG.md",
|
|
"docs/.generated/noise.md",
|
|
"locales/en.json",
|
|
"src/history.ts",
|
|
"src/no-date.ts",
|
|
"src/too-far.ts",
|
|
"src/untracked.ts",
|
|
"test/fixtures/noise.ts",
|
|
]),
|
|
);
|
|
expect(candidates.filter((candidate) => candidate.source === "compat-registry")).toHaveLength(
|
|
1,
|
|
);
|
|
});
|
|
});
|