Skip to content

Commit 0aa81ba

Browse files
committed
update(Execution): verify Pixi executable on startup
- Updated `PythonInstaller.cs` to ensure Pixi is installed and executable by running `pixi --version` on each call.
1 parent 4f38518 commit 0aa81ba

2 files changed

Lines changed: 30 additions & 9 deletions

File tree

docs/agents/execution-system.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Only **unresolvable failure rollbacks** are surfaced to AI callers via `Executio
8585

8686
## Script Modes
8787

88-
- Python: PEP 723 dependencies through `Parser.py`, Pixi preferred, pip/pyRevit fallback.
88+
- Python: PEP 723 dependencies through `Parser.py`, Pixi preferred, pip/pyRevit fallback. Startup runs `pixi --version` after install check; non-zero exit → pip backend.
8989
- IronPython: Python files ending `_ipy_script.py`.
9090
- F#: `.fsx`, NuGet resolution under `%APPDATA%\RevitDevTool\nuget`, 30 second compile timeout.
9191
- C#: `.csx`, Roslyn compilation cache, 30 second compile timeout.

source/DevTools.Execution/Providers/Python/PythonInstaller.cs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.IO;
22
using System.IO.Compression;
3+
using CliWrap;
34
using DevTools.Execution.Services;
45
using DevTools.Utilities;
56
using Microsoft.Extensions.Logging;
@@ -23,9 +24,9 @@ public static class PythonInstaller
2324
private static string VersionMarkerPath => Path.Combine(GetBinPath(), ".pixi-version");
2425

2526
/// <summary>
26-
/// Ensures pixi is installed at the locked version.
27-
/// Uses a marker file to avoid spawning a child process on every startup.
28-
/// Downloads only when pixi.exe is missing or the marker version differs from <see cref="PixiVersion"/>.
27+
/// Ensures pixi is installed at the locked version and can execute.
28+
/// Downloads when pixi.exe is missing or the marker version differs from <see cref="PixiVersion"/>.
29+
/// Runs <c>pixi --version</c> on every call so enterprise blocks on unknown exe trigger pip fallback.
2930
/// </summary>
3031
public static async Task SetupPixiAsync(ILogger? logger = null)
3132
{
@@ -35,13 +36,33 @@ public static async Task SetupPixiAsync(ILogger? logger = null)
3536
if (IsPixiInstalled())
3637
{
3738
logger?.ZLogInformation($"Pixi v{PixiVersion} already installed.");
38-
return;
39+
}
40+
else
41+
{
42+
logger?.ZLogInformation($"Downloading v{PixiVersion}...");
43+
await DownloadAndInstallAsync(PixiVersion).ConfigureAwait(false);
44+
await File.WriteAllTextAsync(VersionMarkerPath, PixiVersion).ConfigureAwait(false);
45+
logger?.ZLogInformation($"v{PixiVersion} installed.");
46+
}
47+
48+
await VerifyPixiRunnableAsync(logger).ConfigureAwait(false);
49+
}
50+
51+
private static async Task VerifyPixiRunnableAsync(ILogger? logger = null)
52+
{
53+
var result = await Cli.Wrap(PixiExePath)
54+
.WithArguments("--version")
55+
.WithValidation(CommandResultValidation.None)
56+
.ExecuteAsync()
57+
.ConfigureAwait(false);
58+
59+
if (result.ExitCode != 0)
60+
{
61+
throw new InvalidOperationException(
62+
$"pixi --version failed with exit code {result.ExitCode}.");
3963
}
4064

41-
logger?.ZLogInformation($"Downloading v{PixiVersion}...");
42-
await DownloadAndInstallAsync(PixiVersion).ConfigureAwait(false);
43-
await File.WriteAllTextAsync(VersionMarkerPath, PixiVersion).ConfigureAwait(false);
44-
logger?.ZLogInformation($"v{PixiVersion} installed.");
65+
logger?.ZLogDebug($"Pixi runtime verified (exit {result.ExitCode}).");
4566
}
4667

4768
private static bool IsMarkedVersion(string version)

0 commit comments

Comments
 (0)