Skip to content

fix(ci): enable git long paths on Windows runners before checkout - #39

Merged
monsieurleberre merged 3 commits into
devfrom
fix/windows-long-paths-checkout
Aug 30, 2026
Merged

fix(ci): enable git long paths on Windows runners before checkout#39
monsieurleberre merged 3 commits into
devfrom
fix/windows-long-paths-checkout

Conversation

@monsieurleberre

@monsieurleberre monsieurleberre commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

The problem

Both Windows shards (windows-amd64, windows-arm64) of a consuming repository's build-and-test matrix die in actions/checkout at step 2, before Setup .NET ever runs:

error: unable to create file <deeply/nested/generated/path>.cs: Filename too long

The offending paths were 202-212 characters; plus the runner's D:\a\<repo>\<repo>\ workspace prefix (~62 chars), they cross the 260-character Windows MAX_PATH limit. core.longpaths is not enabled on the GitHub-hosted Windows image — the failing log shows git version 2.55.0.windows.5.

The consuming repo has no lever of its own. In csharp-ci.yaml the build-and-test job calls actions/checkout bare, with no with: block, so a caller can reach neither sparse-checkout nor any pre-config hook. The fix has to live here.

Generated-code trees hit this easily: the path depth is derived from source namespaces, so it grows on its own and no single repo can shorten its way out permanently.

The change

A single step in the build-and-test job of csharp-ci.yaml and scala-ci.yaml, immediately before the consumer-repo checkout:

      - name: Enable git long paths on Windows
        if: runner.os == 'Windows'
        working-directory: ${{ github.workspace }}
        run: git config --system core.longpaths true

Why --system and not --global

actions/checkout temporarily reassigns HOME before making its own global git config changes, so a --global value written by an earlier step is simply not in the config HOME that the checkout reads. This is visible in the failing run's own log, immediately after the git version banner and before the fetch:

Temporarily overriding HOME='D:\a\_temp\<guid>' before making global git config changes

--system writes outside HOME and therefore survives the override. On the GitHub-hosted Windows images the runner account is an administrator, so the write succeeds.

Why working-directory: ${{ github.workspace }}

Both build-and-test jobs set a job-level defaults.run.working-directory: ${{ inputs.working-directory }}. This new step runs before checkout, when that subdirectory does not exist yet — so for any caller passing a non-root working-directory the step would fail outright. That combination is real: a caller invoking scala-ci.yaml with a non-root working-directory and a Windows shard would have seen the scala half of this fix dead on arrival.

This mirrors the existing pin on the pack job's Validate inputs step, which is pre-checkout for the same reason, and follows #28's precedent.

Sweep: what changed and what did not

File Windows-reachable? Action
.github/workflows/csharp-ci.yaml Yes — build-and-test is runs-on: ${{ matrix.runner }}; os-list/build-matrix both document windows-latest Changed
.github/workflows/scala-ci.yaml Yes — same matrix shape, and callers do run Windows shards Changed
.github/workflows/go-ci.yaml No — build-and-test and lint take the single runs-on input, documented as ubuntu-latest or the self-hosted Linux pool; no OS matrix Left alone
.github/workflows/terraform-ci.yaml No — same single-runs-on shape Left alone
.github/workflows/build-and-test.yaml No — same single-runs-on shape; this repo's own self-test (actionlint + bash + python) Left alone
.github/workflows/csharp-publish-public.yaml No — publish is hard-pinned runs-on: ubuntu-latest Left alone
.github/workflows/update-badges.yaml No — hard-pinned ubuntu-latest Left alone

Within the two changed files, the other checkouts (normalize, coverage-output, matrix-output, matrix-comment, pack) are all in jobs hard-pinned to ubuntu-latest and need nothing. The Checkout CI helpers step inside build-and-test runs on the same Windows runner but after the new step, so the --system setting already covers it.

Verification

  • actionlint (the repo's own Lint workflow files gate, v1.7.7 contract) — clean, exit 0.
  • python3 -m unittest discover -s test -p '*_test.py' — 62 tests, OK.
  • yaml.safe_load on both edited files — parses.

Not yet exercised on a real Windows runner; that happens when a consumer picks up the moved tag (see below).

Required follow-up for the maintainer

Merging this ships nothing. v2 is a floating tag and every consumer pins @v2. Per CONTRIBUTING.md's release process, the tag has to be moved after merge before any caller sees the fix:

git tag -a v2.X.Y -m "..."
git tag -fa v2 -m "Latest v2 (= v2.X.Y)"
git push origin v2.X.Y v2 --force

Until v2 moves, affected Windows shards keep failing identically.

The GitHub-hosted Windows images ship git 2.55.0.windows.5 with
core.longpaths unset, so actions/checkout aborts with "Filename too
long" on any repo containing a path that, once prefixed with the
runner's D:\a\<repo>\<repo>\ workspace root, crosses the 260-character
MAX_PATH limit. The failure lands in step 2, before any toolchain setup,
so a caller's Windows matrix shards can never start — and a caller has
no lever of its own, because build-and-test invokes actions/checkout
with no `with:` block.

csharp-ci.yaml and scala-ci.yaml now run `git config --system
core.longpaths true` on Windows immediately before that checkout. It has
to be --system: actions/checkout temporarily overrides HOME before
making its own global git config changes, so a --global value written by
an earlier step is not read during the checkout itself.

The step is pinned to `working-directory: ${{ github.workspace }}`
because the job-level `defaults.run.working-directory` points at the
caller's `working-directory` input, which does not exist yet before
checkout — the same pin the `pack` job's `Validate inputs` step already
uses for the same reason.

Only these two workflows expose an OS matrix that can place a shard on a
Windows runner. go-ci.yaml, terraform-ci.yaml and build-and-test.yaml
take a single `runs-on` that resolves to ubuntu-latest or the
self-hosted Linux pool, and every remaining checkout in the repo sits in
a job hard-pinned to ubuntu-latest, so none of them are reachable on
Windows.

Claude-Session: https://claude.ai/code/session_01GTo7LMjhYTY3u5NhakUZeb
@monsieurleberre
monsieurleberre merged commit a94bd69 into dev Aug 30, 2026
2 checks passed
@monsieurleberre
monsieurleberre deleted the fix/windows-long-paths-checkout branch August 30, 2026 14:04
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