Skip to content

Commit 99aa8f8

Browse files
ctruedenclaude
andcommitted
Fix PixiBuilder.build() to fully install the pixi environment
Previously, the content-based code paths (pixi.toml, pyproject.toml, environment.yml) wrote the manifest file but never ran `pixi install`, so .pixi/envs/default was only created later, on demand, when the first `pixi run` was invoked. The fix moves `pixi install` into createEnvironment(), covering all build() exit paths uniformly. Adds testBuildInstallsEnv() which asserts .pixi/envs/default exists immediately after build() returns, before any service is launched. To accommodate this change, WrapTest.testWrapPixi now needs to write a valid pixi.toml instead of an empty one, since pixi install now happens even when wrapping an existing environment, and it rejects empty manifests. So we now write a minimal valid [workspace] declaration to satisfy pixi install, and switch the finally-block cleanup to FilePaths.deleteRecursively because pixi install creates a .pixi/ tree. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 45a9b8d commit 99aa8f8

3 files changed

Lines changed: 45 additions & 6 deletions

File tree

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,23 @@ public Environment wrap(File envDir) throws BuildException {
269269

270270
// -- Helper methods --
271271

272-
private Environment createEnvironment(Pixi pixi, File envDir) {
273-
String base = envDir.getAbsolutePath();
274-
String envName = pixiEnvironment != null ? pixiEnvironment : "default";
272+
private Environment createEnvironment(Pixi pixi, File envDir) throws IOException, InterruptedException {
275273
// Check which manifest file exists (pyproject.toml takes precedence).
276274
File manifestFile = new File(envDir, "pyproject.toml");
277275
if (!manifestFile.exists()) manifestFile = new File(envDir, "pixi.toml");
276+
277+
// Ensure the pixi environment is fully installed.
278+
List<String> installCmd = new ArrayList<>(Arrays.asList(
279+
"install", "--manifest-path", manifestFile.getAbsolutePath()
280+
));
281+
if (pixiEnvironment != null) {
282+
installCmd.add("--environment");
283+
installCmd.add(pixiEnvironment);
284+
}
285+
pixi.exec(installCmd.toArray(new String[0]));
286+
287+
String base = envDir.getAbsolutePath();
288+
String envName = pixiEnvironment != null ? pixiEnvironment : "default";
278289
List<String> launchArgs = new ArrayList<>(Arrays.asList(
279290
pixi.command, "run", "--manifest-path",
280291
manifestFile.getAbsolutePath()

src/test/java/org/apposed/appose/builder/PixiBuilderTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,27 @@ public void testPixiEnvironmentSelection() throws Exception {
217217
cowsayAndAssert(env, "multi-env");
218218
}
219219

220+
/**
221+
* Tests that {@link PixiBuilder#build()} fully installs the pixi environment,
222+
* i.e. that {@code .pixi/envs/default} exists after {@code build()} returns,
223+
* not only after the first {@code pixi run} invocation.
224+
*/
225+
@Test
226+
public void testBuildInstallsEnv() throws Exception {
227+
Environment env = Appose
228+
.pixi("src/test/resources/envs/cowsay-pixi.toml")
229+
.base("target/envs/pixi-build-installs-env")
230+
.logDebug()
231+
.rebuild();
232+
// The default pixi environment directory must exist right after build(),
233+
// before any service is launched. This was the bug: build() used to only
234+
// write pixi.toml, leaving installation to the first pixi run invocation.
235+
File envDir = new File(env.base(), ".pixi/envs/default");
236+
assertTrue(envDir.isDirectory(),
237+
".pixi/envs/default should exist after build(), but was missing: " + envDir);
238+
cowsayAndAssert(env, "installed");
239+
}
240+
220241
/** Tests building from a file:// URL to exercise URL support. */
221242
@Test
222243
public void testURLSupport() throws Exception {

src/test/java/org/apposed/appose/builder/WrapTest.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
import org.apposed.appose.TestBase;
3636
import org.junit.jupiter.api.Test;
3737

38+
import org.apposed.appose.util.FilePaths;
39+
3840
import java.io.File;
41+
import java.io.FileWriter;
3942

4043
import static org.junit.jupiter.api.Assertions.assertEquals;
4144
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -53,7 +56,12 @@ public void testWrapPixi() throws Exception {
5356
File pixiDir = new File("target/test-wrap-pixi");
5457
pixiDir.mkdirs();
5558
File pixiToml = new File(pixiDir, "pixi.toml");
56-
pixiToml.createNewFile();
59+
try (FileWriter w = new FileWriter(pixiToml)) {
60+
w.write("[workspace]\n");
61+
w.write("name = \"test-wrap-pixi\"\n");
62+
w.write("channels = [\"conda-forge\"]\n");
63+
w.write("platforms = [\"linux-64\", \"osx-64\", \"osx-arm64\", \"win-64\"]\n");
64+
}
5765

5866
try {
5967
Environment pixiEnv = Appose.wrap(pixiDir);
@@ -65,8 +73,7 @@ public void testWrapPixi() throws Exception {
6573
assertTrue(pixiEnv.launchArgs().get(0).contains("pixi"),
6674
"Pixi environment should use pixi launcher");
6775
} finally {
68-
pixiToml.delete();
69-
pixiDir.delete();
76+
FilePaths.deleteRecursively(pixiDir);
7077
}
7178
}
7279

0 commit comments

Comments
 (0)