Skip to content

Psmux(fix[gates]): Prove the release gates on a real runner - #16

Merged
tony merged 9 commits into
masterfrom
psmux-release-gates
Aug 22, 2026
Merged

Psmux(fix[gates]): Prove the release gates on a real runner#16
tony merged 9 commits into
masterfrom
psmux-release-gates

Conversation

@tony

@tony tony commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Proves the two release gates that had never executed, and fixes seven defects that only a real Windows runner could surface.

Banner

psmux.exe -V from the published v3.3.8 asset prints exactly what PsmuxCompatibility pins:

tmux 3.3.8
psmux 3.3.8 (66cf613 2026-08-18)

Previously derived from the binary's embedded strings and psmux's print_version() rather than executed.

Runner

A [self-hosted, Windows, X64, psmux] runner is registered and online, with PSMUX_WSL_DISTRIBUTION and PSMUX_WSL_DOTNET_PATH set. CONTRIBUTING.md now says how to provision one, including the prerequisites these runs uncovered.

What the gate found

The psmux job had never run. Each failure below was invisible to the Linux lanes:

Defect Why nothing caught it
Build markers still on aa26cd3 / 2026-08-17 the marker scan only runs against a real client, so the library would have rejected the exact psmux it pins
Smoke asserted the literal 3.3.7 that class only executes against a live psmux
WSL scripts handed to dash with CRLF core.autocrlf is a Windows-only default
A failing leg reported only an exit code the harness buffered stdout, where the assertion is written, and never showed it
SDK installed into C:\Program Files\dotnet hosted runners run elevated
No .NET 8 runtime in the isolated install dir hosted images preinstall it
Cold NuGet restore exceeded the 45 minute limit the isolation is deliberate; it is slower off-hosted

Verification

release.yml was dispatched against this branch with a temporary commit that let validate dry-run and skipped publish. The final run was green across validate, published psmux artifact, full dotnet gate / gate, supported tmux matrix / compatibility and psmux native Windows and WSL, with publish skipped. That commit has been rebased out; this branch contains no dry-run guards.

tony added 9 commits August 22, 2026 14:43
why: The gates section named the runner and its labels but not how to get
one, so the only path to a working release machine was reading release.yml
and inferring it. The psmux job has no hosted fallback, so a missing or
offline runner queues the job rather than failing it, which reads as a hung
release.

what:
- Give the config.cmd invocation, including the label that makes runs-on
  match and the registration token's lifetime
- State the machine's prerequisites, including the WSL side the smoke drives
- Show the command that reports what GitHub actually sees
why: The psmux job never ran before, and its first run failed in
setup-dotnet: global.json pins 10.0.302 with rollForward disabled, so the
action installs that exact SDK rather than using what Windows has, and it
defaults to C:\Program Files\dotnet. A self-hosted runner is an ordinary
user account, so the install was denied and the gate failed before reaching
any psmux code.

what:
- Point DOTNET_INSTALL_DIR at the runner tool cache for that step, which the
  runner account owns and which persists between runs
why: The re-pin changed the version, full commit, banner and hash but left
the short-hash and date markers on aa26cd3 and 2026-08-17. Those markers are
scanned in the binary itself, so LibTmux and the smoke harness would reject
the very client the pin now names. Nothing local caught it: the marker scan
only runs against a real psmux executable, which is Windows-only, so the
first psmux gate run was the first time it executed.

what:
- Point the markers in PsmuxBinaryTrust and Invoke-PsmuxSmoke.ps1 at 66cf613
  and 2026-08-18, which the published v3.3.8 client contains and the old one
  does not
- Move the unit-test fixtures onto the same markers
why: The gate restores into a throwaway NuGet cache so the build proves it
needs nothing already on the machine, which verify_workflows enforces by
requiring the isolation before the solution restore. On a self-hosted
Windows runner that cold restore is far slower than the hosted equivalent
and ran past the 45 minute limit while still making progress.

what:
- Raise the psmux job timeout to 90 minutes and say why the restore is cold
why: The harness passes three shell scripts to /bin/sh inside WSL. git
checks this file out with CRLF wherever core.autocrlf is on, which is the
default on Windows, so the first line arrived as 'case ... in' followed by a
carriage return and dash rejected it with 'word unexpected (expecting in)'.
The gate failed on the checkout's line endings rather than on anything the
harness or psmux did.

what:
- Normalise the three WSL scripts to LF where they are defined, so they are
  correct by construction rather than by how the checkout landed
why: The smoke exercises both target frameworks, but global.json pins only
the 10.x SDK, so an isolated install directory holds no .NET 8 runtime and
the native Windows lane died with framework 'Microsoft.NETCore.App' version
'8.0.0' not found. A hosted image hides this by shipping .NET 8 already.

what:
- Install the 8.0 runtime into the same directory, so the job carries both
  frameworks it runs rather than inheriting one from the machine
why: The smoke asserted the literal 3.3.7 while the pin had moved to 3.3.8,
so the gate failed against the client it is supposed to accept. This is the
third copy of the pin the re-pin missed, and each one was invisible until a
real psmux ran: this class only executes against a live client, so no Linux
lane ever reached the assertion.

what:
- Assert PsmuxCompatibility.SupportedVersion, so the check moves with the pin
  instead of being another value to remember
why: A failed leg said only 'Native Windows .NET smoke exited 1.' A test
runner writes the failing assertion to stdout and leaves stderr empty, and
the harness kept stdout in a buffer it never showed, so the one place this
gate runs is the one place its output was discarded. Diagnosing it needed a
patched copy of the harness.

what:
- Fall back to a bounded tail of stdout when stderr carries no detail
- Pass the captured output at every leg that already passed its error
why: Standing the runner up failed three times on machine setup rather than
on psmux, and each was something a hosted runner supplies: PowerShell 7, a
writable place for the pinned SDK, and the .NET 8 runtime the smoke also
runs on. A reader provisioning a machine should not have to rediscover any
of them from a red gate.

what:
- Require pwsh explicitly and give the install command
- Say a Windows dotnet is not needed, because the job installs the pinned
  SDK into its own tool cache
- Say the listener reads its environment once, so it needs a restart after a
  PATH change
@tony
tony force-pushed the psmux-release-gates branch from 3b2d0a0 to 13bd82c Compare August 22, 2026 19:46
@tony
tony merged commit 05b80e0 into master Aug 22, 2026
21 checks passed
@tony
tony deleted the psmux-release-gates branch August 22, 2026 19:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant