diff --git a/docs/WINDOWS_NODE_TESTING.md b/docs/WINDOWS_NODE_TESTING.md index 7343b2ed4..d5fb27179 100644 --- a/docs/WINDOWS_NODE_TESTING.md +++ b/docs/WINDOWS_NODE_TESTING.md @@ -132,6 +132,7 @@ When the node connects, it advertises these capabilities: ### Full Gateway `system.run` MXC runtime proof - The focused E2E below provisions a fresh WSL Gateway, starts an isolated tray instance, sets a local exec approval rule through MCP, invokes `system.run` through the real Gateway `node.invoke` path, and verifies tray MXC diagnostics show contained `mxc-direct-appc` execution for both allowed execution and denied writes to the tray data directory. - Run it when validating the Gateway/Windows node runtime path, not just direct MCP or shared library behavior. +- GitHub-hosted Actions runners do not provide a working MXC/AppContainer runtime. The regular cloud E2E matrix should report these MXC proofs as skipped while still running the rest of setup-connect. Run the proof on a local MXC-enabled Windows machine. Only set `OPENCLAW_RUN_MXC_E2E=1` in GitHub Actions when using an MXC-enabled self-hosted runner. - When reproducing this manually against an existing Gateway, make sure `gateway.nodes.allowCommands` includes `system.run`, `system.run.prepare`, and `system.which`, then approve any `pending-reapproval` request with `openclaw nodes approve `. The node can advertise `system.run` while the Gateway still blocks it until both gates are updated. ```powershell diff --git a/tests/OpenClaw.E2ETests/E2EFactAttribute.cs b/tests/OpenClaw.E2ETests/E2EFactAttribute.cs index 5e8ae9205..f40c97f45 100644 --- a/tests/OpenClaw.E2ETests/E2EFactAttribute.cs +++ b/tests/OpenClaw.E2ETests/E2EFactAttribute.cs @@ -31,6 +31,8 @@ public MxcE2EFactAttribute() internal static class MxcE2ETestGate { + private const string GitHubActionsEnvVar = "GITHUB_ACTIONS"; + private const string ExplicitGitHubActionsOptInEnvVar = "OPENCLAW_RUN_MXC_E2E"; private static readonly Lazy s_skipReason = new(GetSkipReason); public static string? SkipReason => s_skipReason.Value; @@ -40,6 +42,12 @@ internal static class MxcE2ETestGate if (!E2ETestGate.IsEnabled) return $"E2E tests disabled. Set {E2ETestGate.EnvVar}=1 to enable."; + if (IsEnabled(GitHubActionsEnvVar) && !IsEnabled(ExplicitGitHubActionsOptInEnvVar)) + { + return "MXC E2E test skipped in GitHub Actions because hosted runners do not provide a working MXC/AppContainer runtime. " + + $"Run on a local MXC-enabled Windows machine, or set {ExplicitGitHubActionsOptInEnvVar}=1 only on an MXC-enabled self-hosted runner."; + } + try { var availability = ProbeAvailabilityForE2E(); @@ -167,6 +175,11 @@ private static bool FileExists(string? path) catch { return false; } } + private static bool IsEnabled(string envVar) => + Environment.GetEnvironmentVariable(envVar) is { } value && + (string.Equals(value, "1", StringComparison.OrdinalIgnoreCase) || + string.Equals(value, "true", StringComparison.OrdinalIgnoreCase)); + private static string GetSdkArchString() => System.Runtime.InteropServices.RuntimeInformation.OSArchitecture switch { System.Runtime.InteropServices.Architecture.Arm64 => "arm64",