Skip to content

fix(ci): de-flake the ETXTBSY retry test and harden the Windows PATH join - #1377

Merged
zackees merged 3 commits into
mainfrom
fix/1373-dylint-ui-dll-windows
Aug 23, 2026
Merged

fix(ci): de-flake the ETXTBSY retry test and harden the Windows PATH join#1377
zackees merged 3 commits into
mainfrom
fix/1373-dylint-ui-dll-windows

Conversation

@zackees

@zackees zackees commented Aug 23, 2026

Copy link
Copy Markdown
Member

Does not close #1373 — retitled and rescoped after two failed attempts. What it does:

1. De-flakes a test I introduced (the important part)

a_handle_released_mid_window_lets_the_retry_through, added in #1368, failed on this PR's own ubuntu leg. It's my test, in a required check, so it can redden anyone's PR — the exact class of problem #1366 was about, reintroduced by the fix for it.

The test raced a task releasing a write handle after 10 ms against a second spawn attempt at 25 ms. I wrote that "a loaded runner can delay the release several times over and this still passes". A 2.5x margin was not the margin I thought it was.

The backoff is now injectable and the test uses 500 ms against the same 10 ms hold — fifty times later. Production keeps the 25 ms constant. Verified on Linux (where the test isn't a no-op): 23 pass, 0.51 s.

2. Keeps the cygpath hardening

Joining a native CARGO_HOME to a POSIX $PATH with : really does corrupt the variable — the runner's log shows a bare C entry and a bogus D:\Users\runneradmin\.cargo\bin. Worth fixing on its own; it just wasn't what broke the ui tests.

3. Reverts the Windows ui-test experiment, and records what was ruled out

Two hypotheses, two CI rounds, identical failure both times:

tried result
normalize the PATH separator (cygpath -u) no change
add untargeted debug + debug/deps to PATH no change

Established (and now on #1373): two target-dir layouts coexist — reproduced locally, both directories exist — with the library at <target>/debug while compiletest's PATH points at <target>/<triple>/debug/deps. The failure is LoadLibraryExW on the library itself, i.e. a missing dependency, not a missing file. Whatever that dependency is, it is not in either directory I tried.

I'd rather leave the issue open with hard evidence than keep spending CI rounds on guesses. The scoping comment in the workflow now says what was ruled out so the next attempt doesn't repeat it.

The Windows leg keeps doing what #1359 added it for — compiling Windows-gated workspace source so the lints can see it.

🤖 Generated with Claude Code

Closes #1373.

I filed this blaming a dylint/soldr/compiletest interaction over
`CARGO_BUILD_TARGET`. That was wrong. The bug is in the workflow, and it is
mine — the `PATH` the step exported was corrupt on Windows.

## What actually happened

The step did:

    export PATH="${CARGO_HOME}/bin:${PATH}"

On Windows `CARGO_HOME` is a native path (`C:\Users\runneradmin\.cargo`)
while `$PATH` inside Git Bash is POSIX and `:`-separated. Joining them yields

    C:\Users\runneradmin\.cargo/bin:/usr/bin:...

whose drive-letter colon reads as a separator. The failing run's own log shows
the result — a bare `C` entry followed by a bogus
`D:\Users\runneradmin\.cargo\bin` — which I had noted as "looks mangled" and
then discounted.

With the cargo proxy unreachable, the compiletest driver could not resolve the
lint library's dependencies, and every ui fixture failed with

    could not load library `...\ban_manual_slash_normalize@nightly-2026-04-16.dll`:
    LoadLibraryExW failed

That is a *dependent*-DLL failure, not a missing file — the distinction the
issue said to confirm before assuming a path problem. Confirming it is what
found this.

## Fix

Normalize through `cygpath -u` before prepending, giving
`/c/Users/.../.cargo/bin`, which is safe to join with `:`. `cygpath` is absent
on Linux and macOS, where the path is already POSIX, so the conversion is
guarded.

The sweep step was never affected: it invokes
`"${CARGO_HOME}/bin/cargo-dylint"` as a single argument rather than through
`PATH`. Noted there so the asymmetry does not look accidental.

## The step now runs on Windows

#1359 scoped "Test Dylint libraries" to ubuntu because of this
failure. That reasoning no longer holds, so the scoping is removed and the
lint crates' own ui fixtures run on both legs — which matters more than it
sounds: several of these lints are about path spelling, and their diagnostic
rendering is exactly the kind of thing that differs per-OS.

The four remaining ubuntu-only steps stay that way on the original grounds:
they are Python validators and a rustfmt check, and learn nothing from a
second OS.

## Verified

The mangling mechanism is reproduced locally — the bad expression yields
`C:\Users\runneradmin\.cargo/bin:/usr/bin`, matching the runner's log — and
the corrected script produces `/c/Users/.../.cargo/bin`. Running the step's
shell verbatim over all 27 lint crates on a Windows host: 27 pass, 0 failures.

The runner is still the final oracle for a runner-specific interaction, which
is why the step is enabled in the same change rather than after it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ba4882be-dad6-4be1-9309-062dad44dec4

📥 Commits

Reviewing files that changed from the base of the PR and between 0770ef0 and 2ef39de.

📒 Files selected for processing (1)
  • .github/workflows/dylint.yml

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The Dylint workflow now runs library tests on both matrix platforms. It converts Windows Cargo paths to POSIX format before updating PATH and documents direct workspace binary invocation.

Changes

Cross-platform Dylint execution

Layer / File(s) Summary
Workflow path and test updates
.github/workflows/dylint.yml
The workflow removes the Ubuntu-only test condition. It converts Windows CARGO_HOME paths with cygpath -u before updating PATH. The workspace invocation comments describe direct binary execution without a Windows .exe suffix.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 2ef39

This change fixes Windows PATH handling and restores the lint UI tests on Windows; no actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (1 skipped: 1 unsupported.)
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the Windows PATH fix, which is a primary change in the pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1373-dylint-ui-dll-windows

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The first attempt fixed a real PATH-mangling hazard but not this failure —
the Windows leg failed identically, which the PR's own Windows run showed.

The log makes the mechanism explicit. compiletest invokes the driver with

    PATH=...target/dylint-tests\x86_64-pc-windows-msvc\debug;...\debug\deps;...

while the lint library it is told to load is at

    target/dylint-tests\debug\ban_manual_slash_normalize@nightly-2026-04-16.dll

Two target-dir layouts coexist: soldr builds the test binary under
`<target>/<triple>/debug`, and each lint's `fn ui` clears
`CARGO_BUILD_TARGET` so the *library* it rebuilds lands in `<target>/debug` —
the only place Dylint 6.0.1 looks. Both directories exist, which is
reproducible locally.

So the library loads from one tree while its sibling rustc dylibs are searched
for in the other. `LoadLibraryExW failed` names the library and reads like a
missing file; it is a missing *dependency*. That is the distinction the issue
said to confirm before assuming a path problem, and it is what the first
attempt got wrong.

Adding the untargeted `debug` and `debug/deps` to PATH makes both trees
resolvable whichever one an artifact came from. Harmless on Linux and macOS,
where the loader uses rpath and the directories go unused.

Keeping the `cygpath -u` normalization from the first commit: joining a native
`CARGO_HOME` to a POSIX `$PATH` with `:` really does corrupt the variable —
the runner's own log shows a bare `C` entry — it just was not what broke this.

Refs #1373

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@zackees

zackees commented Aug 23, 2026

Copy link
Copy Markdown
Member Author

First attempt was wrong, and this PR's own Windows leg said so — which is why I enabled the step in the same change rather than after it.

The cygpath fix addresses a real hazard (the runner's log genuinely shows a mangled C entry from joining a native CARGO_HOME to a POSIX $PATH with :), but it wasn't what broke the ui tests. The failure log spells out the actual mechanism:

PATH=...target/dylint-tests\x86_64-pc-windows-msvc\debug;...\debug\deps;...
could not load library `...target/dylint-tests\debug\ban_manual_slash_normalize@...dll`

Two target-dir layouts coexist. soldr builds the test binary under <target>/<triple>/debug; each lint's fn ui clears CARGO_BUILD_TARGET so the library it rebuilds lands in <target>/debug — the only place Dylint 6.0.1 looks. I confirmed both directories exist by reproducing the layout locally.

So the library loads from one tree while its sibling rustc dylibs are searched for in the other. LoadLibraryExW failed names the library and reads like a missing file; it's a missing dependency — exactly the distinction I wrote into the issue and then failed to apply.

Fix: put the untargeted debug and debug/deps on PATH so both trees resolve whichever one an artifact came from. No-op on Linux/macOS, where the loader uses rpath.

I can't prove this one locally — the ui tests already pass on my Windows box because the dependencies happen to resolve there, which is precisely why the bug is runner-specific. CI is the oracle again.

Two things, and the second is a retreat.

## De-flake the retry test (the important one)

`a_handle_released_mid_window_lets_the_retry_through`, which I added in
#1368, failed on this PR's ubuntu leg. It is my test, in a
required check, so it can redden anyone's PR — the exact class of problem
#1366 was about, reintroduced by the fix for it.

The test raced a task that releases a write handle after 10 ms against a
second spawn attempt at 25 ms. I called that "a loaded runner can delay the
release several times over and this still passes". A 2.5x margin was not the
margin I thought it was.

The backoff is now injectable, and the test uses 500 ms against the same 10 ms
hold — fifty times later, not two and a half. Production still uses the 25 ms
constant. Verified on Linux, where the test is not a no-op: 23 pass, 0.51 s.

## Stop guessing at #1373

The Windows ui-test step goes back to ubuntu-only. Two hypotheses tried, two
CI rounds, same failure both times:

- normalizing the PATH separator with `cygpath -u` — a real hazard, the
  runner's log genuinely shows a mangled `C` entry, but not this bug;
- adding the untargeted `debug` and `debug/deps` to PATH — no change at all.

What is established and now recorded on the issue: two target-dir layouts
coexist (reproduced locally), the library is at `<target>/debug` while
compiletest's PATH points at `<target>/<triple>/debug/deps`, and the failure
is `LoadLibraryExW` on the library itself — a missing dependency, not a
missing file. Whatever that dependency is, it is not in either directory I
tried.

The `cygpath` normalization stays: it fixes a genuine latent corruption on
Windows even though it did not fix this. The scoping comment now says what was
ruled out, so the next attempt does not repeat it.

The Windows leg keeps doing the thing #1359 added it for — compiling
Windows-gated workspace source so the lints can see it.

Refs #1373

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@zackees zackees changed the title fix(ci): stop mangling PATH on Windows, and run the lint ui tests there fix(ci): de-flake the ETXTBSY retry test and harden the Windows PATH join Aug 23, 2026
@zackees
zackees merged commit 6cb01d9 into main Aug 23, 2026
97 checks passed
@zackees
zackees deleted the fix/1373-dylint-ui-dll-windows branch August 23, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

dylint: lint crates' ui fixtures cannot load their .so/.dll on windows runners (CARGO_BUILD_TARGET vs dylint's probe path)

1 participant