Skip to content

Commit de9b93a

Browse files
ctruedenclaude
andcommitted
Add PixiInstallMonitor for pixi install progress tracking
Parse pixi's -vv stderr output for phase-transition signals (solving, conda install, PyPI download/install) and poll conda-meta/ directory to track conda package installation in real time. PixiBuilder auto- injects -vv and wires the monitor when progress subscribers exist. Closes apposed/appose#30. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 98b9c22 commit de9b93a

2 files changed

Lines changed: 430 additions & 1 deletion

File tree

src/main/java/org/apposed/appose/builder/PixiBuilder.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,32 @@ public Environment wrap(File envDir) throws BuildException {
269269

270270
// -- Helper methods --
271271

272+
/** Returns a new list with an additional flag appended. */
273+
private static List<String> withFlag(List<String> flags, String flag) {
274+
List<String> result = new ArrayList<>(flags);
275+
result.add(flag);
276+
return result;
277+
}
278+
272279
private Environment createEnvironment(Pixi pixi, File envDir) throws IOException, InterruptedException {
273280
// Check which manifest file exists (pyproject.toml takes precedence).
274281
File manifestFile = new File(envDir, "pyproject.toml");
275282
if (!manifestFile.exists()) manifestFile = new File(envDir, "pixi.toml");
276283

284+
// Set up install progress monitoring when subscribers are registered.
285+
PixiInstallMonitor monitor = null;
286+
if (!progressSubscribers.isEmpty()) {
287+
// Inject -vv if not already present, so stderr emits phase signals.
288+
if (!flags.contains("-v") && !flags.contains("-vv") && !flags.contains("-vvv")) {
289+
pixi.setFlags(withFlag(flags, "-vv"));
290+
}
291+
292+
String envName = pixiEnvironment != null ? pixiEnvironment : "default";
293+
monitor = new PixiInstallMonitor(envDir, envName, progressSubscribers,
294+
msg -> errorSubscribers.forEach(sub -> sub.accept(msg)));
295+
pixi.setErrorConsumer(monitor::intercept);
296+
}
297+
277298
// Ensure the pixi environment is fully installed.
278299
List<String> installCmd = new ArrayList<>(Arrays.asList(
279300
"install", "--manifest-path", manifestFile.getAbsolutePath()
@@ -282,7 +303,18 @@ private Environment createEnvironment(Pixi pixi, File envDir) throws IOException
282303
installCmd.add("--environment");
283304
installCmd.add(pixiEnvironment);
284305
}
285-
pixi.exec(installCmd.toArray(new String[0]));
306+
try {
307+
pixi.exec(installCmd.toArray(new String[0]));
308+
}
309+
finally {
310+
if (monitor != null) {
311+
monitor.shutdown();
312+
// Restore the original error consumer.
313+
pixi.setErrorConsumer(msg -> errorSubscribers.forEach(sub -> sub.accept(msg)));
314+
// Restore the original flags.
315+
pixi.setFlags(flags);
316+
}
317+
}
286318

287319
String base = envDir.getAbsolutePath();
288320
String envName = pixiEnvironment != null ? pixiEnvironment : "default";

0 commit comments

Comments
 (0)