-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathIndexCommand.java
More file actions
143 lines (119 loc) · 5.83 KB
/
Copy pathIndexCommand.java
File metadata and controls
143 lines (119 loc) · 5.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package io.github.randomcodespace.iq.cli;
import io.github.randomcodespace.iq.analyzer.AnalysisResult;
import io.github.randomcodespace.iq.analyzer.Analyzer;
import io.github.randomcodespace.iq.config.CodeIqConfig;
import org.springframework.stereotype.Component;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import java.nio.file.Path;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.concurrent.Callable;
/**
* Index a codebase: scan files, run detectors, write results to H2.
* <p>
* This command does NOT start Neo4j. It uses H2 as the primary store,
* processing files in batches to keep memory bounded.
* <p>
* Use {@code enrich} after indexing to load data into Neo4j for graph queries.
*/
@Component
@Command(name = "index", mixinStandardHelpOptions = true,
description = "Index codebase to H2 (no Neo4j, memory-efficient batched processing)")
public class IndexCommand implements Callable<Integer> {
@Parameters(index = "0", defaultValue = ".", description = "Path to codebase root")
private Path path;
@Option(names = {"--no-cache"}, description = "Skip incremental cache (full re-index)")
private boolean noCache;
@Option(names = {"--incremental"}, defaultValue = "true", negatable = true,
description = "Use incremental analysis (default: true)")
private boolean incremental;
@Option(names = {"--parallelism", "-p"},
description = "Max parallel threads (default: auto-detect from CPU)")
private Integer parallelism;
@Option(names = {"--batch-size", "-b"}, defaultValue = "500",
description = "Files per H2 flush batch (default: 500)")
private int batchSize;
@Option(names = {"--graph"}, description = "Path to shared graph directory (for multi-repo)")
private Path graphDir;
@Option(names = {"--service-name"}, description = "Service name tag for nodes (for multi-repo)")
private String serviceName;
@Option(names = {"--verbose", "-v"}, description = "Enable verbose per-file logging")
private boolean verbose;
private final Analyzer analyzer;
private final CodeIqConfig config;
public IndexCommand(Analyzer analyzer, CodeIqConfig config) {
this.analyzer = analyzer;
this.config = config;
}
@Override
public Integer call() {
if (verbose) {
((ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory.getLogger("io.github.randomcodespace.iq"))
.setLevel(ch.qos.logback.classic.Level.DEBUG);
}
Path root = path.toAbsolutePath().normalize();
CliOutput.configureFromOptions(config, graphDir, serviceName);
// Use configured batch size if not overridden on command line
int effectiveBatchSize = batchSize > 0 ? batchSize : config.getBatchSize();
NumberFormat nf = NumberFormat.getIntegerInstance(Locale.US);
int cores = parallelism != null ? parallelism : Runtime.getRuntime().availableProcessors();
// --no-cache overrides --incremental and deletes existing cache DB
boolean useIncremental = incremental && !noCache;
if (noCache) {
Path cacheDir = root.resolve(config.getCacheDir());
if (java.nio.file.Files.exists(cacheDir)) {
try {
try (var walk = java.nio.file.Files.walk(cacheDir)) {
var logger = org.slf4j.LoggerFactory.getLogger(IndexCommand.class);
walk.sorted(java.util.Comparator.reverseOrder())
.forEach(p -> {
try {
java.nio.file.Files.deleteIfExists(p);
} catch (java.io.IOException ex) {
logger.debug("Could not delete cache entry {}: {}", p, ex.getMessage());
}
});
}
CliOutput.info(" Deleted existing cache at " + cacheDir);
} catch (Exception e) {
CliOutput.warn(" Could not delete cache: " + e.getMessage());
}
}
}
CliOutput.step("[*]", "Indexing " + root + " ...");
CliOutput.info(" (batch size: " + effectiveBatchSize + " files, "
+ cores + " cores, H2 store, config-first smart pipeline)");
if (useIncremental) {
CliOutput.info(" (incremental mode -- use --no-cache for full re-index)");
}
AnalysisResult result = analyzer.runSmartIndex(root, parallelism, effectiveBatchSize,
useIncremental, msg -> {
if (msg.startsWith("Phase 1")) {
CliOutput.step("[*]", "@|bold " + msg + "|@");
} else if (msg.startsWith("Phase 2")) {
CliOutput.step("[+]", "@|bold " + msg + "|@");
} else if (msg.startsWith("Processing module")) {
CliOutput.step("[:]", msg);
} else if (msg.startsWith("Processing batch")) {
CliOutput.step("[~]", msg);
} else if (msg.startsWith("Keyword filter")) {
CliOutput.step("[!]", "@|green " + msg + "|@");
} else if (msg.startsWith("Cache hits")) {
CliOutput.step("[!]", "@|green " + msg + "|@");
} else if (msg.startsWith("Service:")) {
CliOutput.info(" " + msg);
} else if (msg.startsWith("Smart index complete")) {
// handled below
} else {
CliOutput.info(msg);
}
});
CliOutput.printResultSummary(result, nf);
CliOutput.info(" Store: H2 (.code-iq/cache/analysis-cache)");
System.out.println();
CliOutput.info(" Next step: code-iq enrich " + root);
return 0;
}
}