Skip to content

Commit 29838ab

Browse files
Copilotretsohuang
andcommitted
fix: address PR review comments for isomorphic-git migration
- Fix conditional execution check using fileURLToPath to properly compare file:// URL with file path - Make loadConfig parameter handling consistent by using nullable configPath - Separate filesystem concerns: add pluginFs parameter for reading plugin files from real filesystem - Update tests to use realFs for plugin files instead of trying to create them in memfs - Remove test setup code that attempted to create plugin files in memfs - Rename test suite from "CLI integration (help commands)" to "module exports" - Add .gitignore to exclude node_modules and package-lock.json Co-authored-by: retsohuang <785920+retsohuang@users.noreply.github.com>
1 parent df4a441 commit 29838ab

3 files changed

Lines changed: 62 additions & 44 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
package-lock.json

plugins/code-review-tools/scripts/src/cli.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
type PromiseFsClient,
88
} from "isomorphic-git"
99
import * as nodeFs from "node:fs"
10+
import { fileURLToPath } from "node:url"
1011
import { join } from "node:path"
1112
import {
1213
DEFAULT_CONFIG,
@@ -52,15 +53,16 @@ function error(message: string): ErrorOutput {
5253
}
5354

5455
function loadConfig(
55-
configPath: string = ".claude/code-review-tools/config.json",
56+
configPath: string | undefined = undefined,
5657
fs: FsLike = defaultFs,
5758
): Output<ReviewConfig> {
59+
const actualConfigPath = configPath ?? ".claude/code-review-tools/config.json"
5860
try {
59-
if (!fs.existsSync(configPath)) {
61+
if (!fs.existsSync(actualConfigPath)) {
6062
return success(DEFAULT_CONFIG)
6163
}
6264

63-
const userConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"))
65+
const userConfig = JSON.parse(fs.readFileSync(actualConfigPath, "utf-8"))
6466
const config = parseConfig(userConfig)
6567

6668
return success(config)
@@ -150,6 +152,7 @@ function buildRules(
150152
config: ReviewConfig,
151153
pluginRoot: string,
152154
fs: FsLike = defaultFs,
155+
pluginFs: FsLike = defaultFs,
153156
): Output<BuildRulesResult> {
154157
try {
155158
const rulesSections: string[] = []
@@ -176,8 +179,8 @@ function buildRules(
176179
for (const rule of builtInRules) {
177180
if (config.builtInRules?.[rule.key]) {
178181
const rulePath = join(pluginRoot, "rules", rule.file)
179-
if (fs.existsSync(rulePath)) {
180-
const content = fs.readFileSync(rulePath, "utf-8")
182+
if (pluginFs.existsSync(rulePath)) {
183+
const content = pluginFs.readFileSync(rulePath, "utf-8")
181184
rulesSections.push(`# ${rule.name} Rules\n\n${content}\n\n---\n`)
182185
enabledCount++
183186
}
@@ -228,23 +231,27 @@ function loadTemplate(
228231
pluginRoot: string,
229232
defaultTemplateName: string,
230233
fs: FsLike = defaultFs,
234+
pluginFs: FsLike = defaultFs,
231235
): string {
232236
let templatePath: string
237+
let usePluginFs = false
233238

234239
if (customTemplate) {
235240
templatePath = `.claude/code-review-tools/templates/${customTemplate}`
236241
} else {
237242
templatePath = `${pluginRoot}/templates/${defaultTemplateName}`
243+
usePluginFs = true
238244
}
239245

240-
if (fs.existsSync(templatePath)) {
241-
return fs.readFileSync(templatePath, "utf-8")
246+
const fsToUse = usePluginFs ? pluginFs : fs
247+
if (fsToUse.existsSync(templatePath)) {
248+
return fsToUse.readFileSync(templatePath, "utf-8")
242249
}
243250

244251
const defaultPath = `${pluginRoot}/templates/${defaultTemplateName}`
245-
if (fs.existsSync(defaultPath)) {
252+
if (pluginFs.existsSync(defaultPath)) {
246253
console.error(`WARNING: Template not found: ${templatePath}, using default`)
247-
return fs.readFileSync(defaultPath, "utf-8")
254+
return pluginFs.readFileSync(defaultPath, "utf-8")
248255
}
249256

250257
throw new Error(`Template not found: ${defaultPath}`)
@@ -255,6 +262,7 @@ async function prepareReview(
255262
pluginRoot: string,
256263
dir: string = DEFAULT_DIR,
257264
fs: FsLike = defaultFs,
265+
pluginFs: FsLike = defaultFs,
258266
): Promise<Output<PrepareReviewResult>> {
259267
try {
260268
const configResult = loadConfig(undefined, fs)
@@ -268,7 +276,7 @@ async function prepareReview(
268276
return error(`Failed to collect commits: ${commitsResult.error}`)
269277
}
270278

271-
const rulesResult = buildRules(config, pluginRoot, fs)
279+
const rulesResult = buildRules(config, pluginRoot, fs, pluginFs)
272280
if (!rulesResult.success) {
273281
return error(`Failed to build rules: ${rulesResult.error}`)
274282
}
@@ -278,12 +286,14 @@ async function prepareReview(
278286
pluginRoot,
279287
"report-template.md",
280288
fs,
289+
pluginFs,
281290
)
282291
const summaryTemplate = loadTemplate(
283292
config.reports?.summaryTemplate,
284293
pluginRoot,
285294
"summary-template.md",
286295
fs,
296+
pluginFs,
287297
)
288298

289299
return success({
@@ -405,7 +415,7 @@ async function main(): Promise<void> {
405415
}
406416
}
407417

408-
if (import.meta.url.endsWith(process.argv[1])) {
418+
if (fileURLToPath(import.meta.url) === process.argv[1]) {
409419
main().catch((err) => {
410420
console.error("Fatal error:", (err as Error).message)
411421
process.exit(1)

plugins/code-review-tools/scripts/tests/cli.test.ts

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
22
import { add, commit, init, setConfig } from "isomorphic-git"
33
import { Volume, createFsFromVolume } from "memfs"
4+
import * as nodeFs from "node:fs"
45
import { join } from "node:path"
56
import {
67
collectCommits,
@@ -11,6 +12,15 @@ import {
1112

1213
const PLUGIN_ROOT = join(__dirname, "../../")
1314

15+
// Real filesystem for reading plugin files
16+
const realFs: FsLike = {
17+
promises: nodeFs.promises,
18+
existsSync: nodeFs.existsSync,
19+
readFileSync: nodeFs.readFileSync,
20+
writeFileSync: nodeFs.writeFileSync,
21+
mkdirSync: nodeFs.mkdirSync,
22+
} as FsLike
23+
1424
let fs: FsLike
1525
let vol: Volume
1626
let testCommits: string[] = []
@@ -67,34 +77,6 @@ beforeEach(async () => {
6777
})
6878

6979
testCommits = [commit3, commit2, commit1]
70-
71-
const pluginRulesDir = join(PLUGIN_ROOT, "rules")
72-
const pluginTemplatesDir = join(PLUGIN_ROOT, "templates")
73-
74-
fs.mkdirSync(pluginRulesDir, { recursive: true })
75-
fs.mkdirSync(pluginTemplatesDir, { recursive: true })
76-
77-
fs.writeFileSync(
78-
join(pluginRulesDir, "component-extraction-rules.md"),
79-
"# Component Extraction Rules\nVerify component extraction.",
80-
)
81-
fs.writeFileSync(
82-
join(pluginRulesDir, "component-reuse-rules.md"),
83-
"# Component Reuse Rules\nCheck for reuse opportunities.",
84-
)
85-
fs.writeFileSync(
86-
join(pluginRulesDir, "ai-slop-rules.md"),
87-
"# AI Slop Rules\nDetect AI patterns.",
88-
)
89-
90-
fs.writeFileSync(
91-
join(pluginTemplatesDir, "report-template.md"),
92-
"# Report Template\n{commitRange}",
93-
)
94-
fs.writeFileSync(
95-
join(pluginTemplatesDir, "summary-template.md"),
96-
"# Summary Template\n{commits}",
97-
)
9880
})
9981

10082
afterEach(() => {
@@ -223,7 +205,13 @@ describe("prepareReview", () => {
223205
test("prepares review data successfully with default config", async () => {
224206
const startCommit = testCommits[1].slice(0, 7)
225207

226-
const result = await prepareReview(startCommit, PLUGIN_ROOT, TEST_DIR, fs)
208+
const result = await prepareReview(
209+
startCommit,
210+
PLUGIN_ROOT,
211+
TEST_DIR,
212+
fs,
213+
realFs,
214+
)
227215

228216
expect(result.success).toBe(true)
229217
if (result.success) {
@@ -267,7 +255,13 @@ describe("prepareReview", () => {
267255

268256
const startCommit = testCommits[1].slice(0, 7)
269257

270-
const result = await prepareReview(startCommit, PLUGIN_ROOT, TEST_DIR, fs)
258+
const result = await prepareReview(
259+
startCommit,
260+
PLUGIN_ROOT,
261+
TEST_DIR,
262+
fs,
263+
realFs,
264+
)
271265

272266
expect(result.success).toBe(true)
273267
if (result.success) {
@@ -282,7 +276,13 @@ describe("prepareReview", () => {
282276
test("returns correct commit list", async () => {
283277
const startCommit = testCommits[2].slice(0, 7)
284278

285-
const result = await prepareReview(startCommit, PLUGIN_ROOT, TEST_DIR, fs)
279+
const result = await prepareReview(
280+
startCommit,
281+
PLUGIN_ROOT,
282+
TEST_DIR,
283+
fs,
284+
realFs,
285+
)
286286

287287
expect(result.success).toBe(true)
288288
if (result.success) {
@@ -292,7 +292,13 @@ describe("prepareReview", () => {
292292
})
293293

294294
test("returns error for invalid commit", async () => {
295-
const result = await prepareReview("invalid", PLUGIN_ROOT, TEST_DIR, fs)
295+
const result = await prepareReview(
296+
"invalid",
297+
PLUGIN_ROOT,
298+
TEST_DIR,
299+
fs,
300+
realFs,
301+
)
296302

297303
expect(result.success).toBe(false)
298304
if (!result.success) {
@@ -301,7 +307,7 @@ describe("prepareReview", () => {
301307
})
302308
})
303309

304-
describe("CLI integration (help commands)", () => {
310+
describe("module exports", () => {
305311
test("CLI exports are available", () => {
306312
expect(prepareReview).toBeDefined()
307313
expect(collectCommits).toBeDefined()

0 commit comments

Comments
 (0)