Skip to content

[P2] Fail immediately when any release validation or package push exits nonzero #20

Description

@AGiorgetti

Priority and scope

P2: Native command failures can be overwritten by a later success, allowing failed API validation or an incomplete publication to be reported as successful.

Reviewed default develop at 612e5c03bba3e811741385cd7440ffb5af43b8df. main at b9697f2f91aa737360479a434f7d3cc01ccaa8fd has the identical source tree, so this is not develop-only.

Evidence and cause

The API compatibility loop does not check $LASTEXITCODE; subsequent restore/run/publish commands overwrite it. Only the last AOT executable is explicitly checked. The NuGet push loop likewise checks neither each result nor an accumulated failure. GitHub documents that its PowerShell wrapper exits with the last native exit code; PowerShell's native-error preference defaults to false. The checked-in local release script already uses per-command Assert-NativeSuccess, but the workflow does not.

Minimal reproduction (proposed, not executed)

In an isolated test of the workflow script, mock the first apicompat command to set LASTEXITCODE=1 and all later native commands to 0. Separately mock the first or second NuGet push to fail and the final push to succeed. Do not send packages to NuGet.

Actual behavior predicted from source

The script continues past the failed command. A later success resets LASTEXITCODE to zero, so the runner can mark the step successful. A failed compatibility check is not a release gate, and an earlier failed push can be hidden.

Expected behavior

Any failed validation must block artifact promotion/publication; any failed package push must fail the job without concealing which package failed.

Fix direction

Check and throw immediately after every native command, or explicitly enable reliable native failure handling for the pinned PowerShell runtime. Audit all multiline/loop native calls. Reuse the local script's established failure-check pattern where practical.

Regression acceptance

  • Inject nonzero exits independently for both API checks, restore, run, trimmed publish/execution, AOT publish/execution, and each package push
  • Assert the first failure fails the step and prevents subsequent publication
  • Successful paths still pass
  • Tests execute or evaluate the script behavior, not only text fragments

Duplicate review

Related to closed #17/#18 release hardening, but distinct from their fixed RID, AOT executable location, artifact-action, and duplicate-symbol-push issues. All 18 existing issue/PR records and 72 issue-conversation comments were inspected; none currently tracks this specific unresolved case.

Verification limits

This is a static source finding. The reproduction was not compiled or run, and no repository code, release workflow, or benchmark was executed in this review. The existing exact-head CI run passed its build/test and Roslyn-host jobs; release pack, artifact-verification, and publish jobs were skipped. That existing run does not validate this proposed regression.

Prepared with OpenAI Codex.


Validation update (2026-10-02)

Confirmed by direct PowerShell execution with mocked native commands. The earlier static-only verification statement is superseded for these script-control-flow checks. The source remains 612e5c03bba3e811741385cd7440ffb5af43b8df; no fix was applied.

Executed checks

The original Validate final package consumers and Publish exact packages PowerShell bodies were extracted from the pinned workflow and executed under PowerShell 7.6.6 on Debian 13 x86_64. Only GitHub expression values were bound to fixture paths/version. Separate native-process stubs replaced dotnet and consumer executables; the scripts were not translated into another language.

The native-error preference was observed as PSNativeCommandUseErrorActionPreference=False. The wrapper reproduced GitHub's documented error preference and final native-exit-code handling.

Consumer validation: 9 cases completed, consisting of the all-success control plus an independent exit-23 injection at each of the 8 native command positions.

Failed native operation Observed step exit Result
First API compatibility check 0 Later commands ran; failure masked
Second API compatibility check 0 Later commands ran; failure masked
Restore 0 Later commands ran; failure masked
Run 0 Later commands ran; failure masked
Trimmed publish 0 Missing-DLL mock also returned 24; later AOT success masked both
Trimmed execution 0 Later commands ran; failure masked
AOT publish 1 Missing output directory caused a terminating Get-ChildItem error
AOT execution 1 Existing explicit $LASTEXITCODE guard threw

Publish loop: 4 cases completed. Native exit sequences [23,0,0] and [0,23,0] both produced step exit 0. [0,0,23] produced exit 23. The all-success [0,0,0] control produced exit 0. Thus either early package-push failure can be hidden by the last successful push.

Scope and limits

These 13 cases execute the original PowerShell sequencing and guards, but native .NET/API compatibility/build/push operations and consumer artifacts are mocked. The inherited process environment was replaced; dotnet resolution was checked against the mock; the API-key value was a fixed dummy. No real endpoint was contacted by the stubs, no secrets were used, and no package was published.

This validates the failure-propagation defect, not a real package's compatibility or a hosted Windows release run. Later artifact-state failures can still stop execution, as the AOT controls demonstrate; an earlier native failure is not guaranteed to be masked in every real scenario.

Primary behavior references: GitHub PowerShell shell handling and PowerShell native-command error handling.

Overall cloud-validation scope

The dated evidence above supersedes the earlier static-only verification statement for the listed cases; unexecuted regression-acceptance variants remain proposed. The unchanged full solution built in Release with zero warnings/errors. The existing suites recorded 653 passes and 2 failures across 655 tests, with no skips. Both failures, TrimmedConsumerPublishesAndRunsWithoutLiteMapperWarnings and NativeAotConsumerPublishesAndRunsWithoutLiteMapperWarnings, were cloud infrastructure blocked: ILLink's out-of-process ComputeManagedAssemblies task host failed with MSB4216 and Unix-domain pipe SocketException (13), permission denied. Trimming/AOT behavior was not established, and these tests are not intrinsically Windows-only. The repository's separate win-x64 final-package release lane still requires a Windows execution environment and Windows/MSVC toolchain.

The 20 focused generator tests comprised 14 intentional contract assertion failures reproducing defects and 6 passing controls/measurement cases; they are not an all-green acceptance suite. No production fixes or releases were made.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority:p2AgentStack priority P2type:bugAgentStack work item type: bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions