diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ee4a42da564..7b182a4d98c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -75,7 +75,16 @@ jobs: run: | ./dev.sh test working-directory: src - if: matrix.runtime == 'arm64' || matrix.runtime == 'amd64' + # Compares against matrix.runs_on (bare arch names: amd64/arm64/ppc64le/s390x), not + # matrix.runtime (always 'linux-*'-prefixed) -- the two were previously confused here, + # which meant this condition never evaluated true for any matrix leg and L0 silently + # never ran in this workflow. linux-x64/linux-arm64 build/run natively on matching + # self-hosted hardware with no patches applied, so they're safe to test directly here. + # linux-arm (32-bit armv7l, cross-targeted even though it builds on arm64 hardware), + # ppc64le, and s390x are intentionally left out: they require the arm/ppc64le/s390x + # patches to be applied first and their 32-bit/cross-arch test-tooling behavior under + # dotnet test is not yet validated. + if: matrix.runtime == 'linux-x64' || matrix.runtime == 'linux-arm64' # Create runner package tar.gz/zip - name: Package Release diff --git a/src/Test/L0/Worker/JobExtensionL0.cs b/src/Test/L0/Worker/JobExtensionL0.cs index fa67aa33ad0..b99682b681c 100644 --- a/src/Test/L0/Worker/JobExtensionL0.cs +++ b/src/Test/L0/Worker/JobExtensionL0.cs @@ -15,8 +15,17 @@ namespace GitHub.Runner.Common.Tests.Worker { - public sealed class JobExtensionL0 + public sealed class JobExtensionL0 : IDisposable { + // JobExtension.InitializeJob reads these environment variables directly to decide whether to inject + // extra "job started"/"job completed" hook steps. Some self-hosted runner hosts configure these hooks + // for their own lifecycle management, and since they're real process-level environment variables they + // would otherwise leak into this test process and silently change the expected step count. + private const string JobStartedHookVariable = "ACTIONS_RUNNER_HOOK_JOB_STARTED"; + private const string JobCompletedHookVariable = "ACTIONS_RUNNER_HOOK_JOB_COMPLETED"; + private readonly string _originalJobStartedHook; + private readonly string _originalJobCompletedHook; + private IExecutionContext _jobEc; private Pipelines.AgentJobRequestMessage _message; @@ -33,6 +42,24 @@ public sealed class JobExtensionL0 private Pipelines.Snapshot _requestedSnapshot; private CancellationTokenSource _tokenSource; + + public JobExtensionL0() + { + // xUnit creates a new instance per test, so this runs before every test: capture whatever the host + // environment has configured and force a clean (unset) state so tests aren't affected by job hooks + // that a self-hosted runner's own environment may have configured for itself. + _originalJobStartedHook = Environment.GetEnvironmentVariable(JobStartedHookVariable); + _originalJobCompletedHook = Environment.GetEnvironmentVariable(JobCompletedHookVariable); + Environment.SetEnvironmentVariable(JobStartedHookVariable, null); + Environment.SetEnvironmentVariable(JobCompletedHookVariable, null); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable(JobStartedHookVariable, _originalJobStartedHook); + Environment.SetEnvironmentVariable(JobCompletedHookVariable, _originalJobCompletedHook); + } + private TestHostContext CreateTestContext([CallerMemberName] String testName = "") { var hc = new TestHostContext(this, testName);