Skip to content

Move the GitLab Windows jobs to GLR-UV, and build msvc 192 in CI - #129

Merged
wdolinar merged 3 commits into
masterfrom
gitlab-uv-runner
Sep 2, 2026
Merged

Move the GitLab Windows jobs to GLR-UV, and build msvc 192 in CI#129
wdolinar merged 3 commits into
masterfrom
gitlab-uv-runner

Conversation

@gagelarsen

@gagelarsen gagelarsen commented Sep 1, 2026

Copy link
Copy Markdown
Member

Two related changes to the generated GitLab pipeline, plus a third commit answering review.

1. a3b1f28 — GLR-UV runner and uv

The per-ABI GLR-py310 / GLR-py313 images are gone, replaced by a single GLR-UV image carrying uv and pre-loaded 3.10 / 3.13 / 3.14 interpreters.

Those images existed for one reason: to make python on PATH the matrix version. The recipe hands CMake Python3_EXECUTABLE = sys.executable and the generated CMakeLists.txt requires find_package(Python3 ... EXACT REQUIRED), so the interpreter running conan is the ABI being built — the ABI had to be chosen by picking an image and could not be changed inside the job. That is what PY_TAG and image: GLR-${PY_TAG} were for, and both are gone:

  image: GLR-UV
  script:
    - uv --version
    - uv venv --python ${PYTHON_TARGET_VERSION} .venv
    - source .venv/Scripts/activate
    - uv pip install "conan~=2.31.0" ...
  tags:
    - WinVM        # unchanged: the tag routes to the fleet, image: picks the VM

The venv is safe for the pybind path: the recipe's Python3_INCLUDE_DIR hint comes from sysconfig.get_path('include'), which under the nt_venv scheme resolves to the base installation's Include (Python.h present), not an empty .venv/Include. Verified against a real venv.

Also drops the hard-coded /c/Users/admin from the deploy job's cache snapshot in favor of ${HOME} — an assumption about the retired runner's service account that shipped an empty artifact, greenly, under any other one.

2. dd01421 — msvc 192 in CI, behind [ci].windows_vs2019

GLR-UV carries VS2019 alongside VS2022, so the matrix that had to be built by hand on a developer workstation can be built on every pipeline instead. Opt-in, defaulting off: it roughly doubles the Windows half of a pipeline, and only the libraries the VS2019-era desktop products consume need msvc 192 binaries.

The jobs are emitted beside the msvc 194 pair and reuse its shape. Four things differ, and all four are load-bearing:

  • build.py --platform windows_vs2019 selects the matrix and derives apply_boost_defaults=False (the legacy boost/1.74.0.3 recipe does not declare the 1.86 options, and Conan fails a build when a profile sets an option no recipe defines) and the upload destination — all from one flag, so the matrix and its destination cannot be set differently.
  • xmsconan_conan_setup --remote-name ... --append adds aquaveo-vs2019. Appended, so it does not become the first stop for every conan install on a shared runner.
  • --package-query compiler.version=... on every Windows publish — see the review response below; this started one-sided and is now symmetric.
  • No wheels. A wheel's tags say nothing about which MSVC built it, so an msvc 192 wheel and an msvc 194 wheel are the same devpi filename. Those stay a deliberate manual step (§16.8).

3. 8779d95 — review response

Thanks — M2 was a real defect and I would have shipped it.

M2 (mirror leak), fixed. The msvc 192 publishes were queried and the msvc 194 ones were not, and the hazard is symmetric — the unqueried direction is the worse one, since it puts msvc 192 binaries on the production aquaveo remote at exit 0. Both Windows jobs now carry their own query, on both the save and the upload, unconditionally rather than only under the opt-in: a guard present in just the configuration that needs it is one refactor from being dropped from the one that does. Both values are read from the packager's configurations rather than written into the template — a stale literal fails silently, since a query naming a version the matrix no longer builds matches nothing and the job goes green having published nothing.

M1 (stale docs), fixed. All four locations: README's VS2019 section, vs2019_build's module docstring, the windows_vs2019 comment in packager.configurations, and VS2019_REMOTE_NAME's in constants.py. README also gains the [ci].windows_vs2019 row. You were right that constants.py was a fourth site — I had updated MSVC_VS2019_VERSION's comment and missed the one above it. I skipped adding the four new flags to README's Conan Setup / Deploy sections, as you suggested: README defers flag reference to docs/USAGE.md, which has them.

Minor items — all applied except three, and the remotes.json question answered:

  • _package_list is now a contextmanager. The cleanup was on CalledProcessError alone, so a conan missing from PATH leaked the file the docstring promised would never be left behind. Both failure modes are now parametrized tests.
  • --platform takes choices from the packager's matrix.
  • The preview test reads the compiler.version column instead of grepping stdout. Worth flagging that the substring form also passed on an empty matrix, which is the failure most worth catching there.
  • The PYTHONPATH pin derives the repo root from the test file, not an imported module's __file__. Good catch — that resolved into site-packages and defeated the pin it was written to provide.
  • Whole-command assertions in test_conan_deploy, and one shared recorder helper instead of two copies.
  • New test that the VS2019 save and restore spell the same tarball.
  • "pip install" filter; the un-flagged conan_setup call asserted by shape; the eleven msvc 192 tests grouped under TestVs2019Ci; the continuation backslash restored in conan_setup's usage docstring.

remotes.json (your open question): conan 2.31 writes it as remotes.json.tmp followed by os.replace (conan/api/subapi/remotes.py, _save), so the file cannot be torn — a reader sees one complete document or the other. But there is no lock around the read-modify-write, so two jobs adding remotes in the same instant can lose one of the two additions. Left as is, deliberately: the lost update is loud (the job that lost its remote fails resolving dependencies rather than publishing somewhere wrong), and the fix — a per-job CONAN_HOME — gives up the shared package cache these Windows builds depend on. Noted at both call sites and in the troubleshooting list.

Three declined, with reasons:

  1. Marking the preview test integration. That marker is defined for "real conan + gcovr … multi-minute wall time"; this test runs no conan and takes about a second, so marking it would exclude it from the default run for no benefit. The premise that it is the suite's only unmocked subprocess.run is also not right — test_build_file_generator.py has others at :1475 and :1792, equally unmarked.
  2. Merging the two if ci_type == "github": blocks. Both predate this PR: origin/master has the same four blocks at :242, :351, :374, :544. This diff adds a nested if inside the existing one, not a new block, so merging them is an unrelated refactor.
  3. Splitting the generated build.py __main__. Pre-existing shape that this diff adds ~15 lines to. Restructuring the script every repo runs has its own blast radius and belongs in its own change.

Ops prerequisites

  1. CONAN_LOGIN_USERNAME_AQUAVEO_VS2019 / CONAN_PASSWORD_AQUAVEO_VS2019 as GitLab CI variables. Branch pipelines pass without them; only the tag-time deploy fails, so the gap would surface at the first release.
  2. [ci].windows_vs2019 = true in the repositories that should get msvc 192. Nothing changes until they do.

Testing

2351 passed, 4 skipped; flake8 clean. conan_deploy and conan_setup are at 100% line and branch coverage.

Four harder pipeline shapes were rendered and checked end to end (split_tests + coverage + wheel_only, deploy = false, linux = false, xvfb + shards): every job's stage: is declared in stages:, every needs: names a job that exists, and the VS2019 deploy job disappears with [ci].deploy = false.

The per-ABI GLR-py310 / GLR-py313 images are gone, replaced by a single
GLR-UV image carrying uv and pre-loaded 3.10 / 3.13 / 3.14 interpreters.

Those images existed for one reason: to make `python` on PATH the matrix
version. The recipe hands CMake `Python3_EXECUTABLE = sys.executable` and
the generated CMakeLists.txt requires `find_package(Python3 ... EXACT
REQUIRED)`, so the interpreter running conan *is* the ABI being built --
which meant the ABI had to be chosen by picking an image and could not be
changed inside the job. That is what PY_TAG and `image: GLR-${PY_TAG}`
were for, and both are now gone: the job establishes the interpreter
itself with `uv venv --python ${PYTHON_TARGET_VERSION}`, activates it,
and installs conan and xmsconan into it with `uv pip install`. GitLab
runs a job's script lines as one shell, so the activation carries.

`uv --version` runs first so a runner without uv fails on a line that
says so rather than four lines later on an install that reads like a
packaging problem. `tags: WinVM` is unchanged -- the tag routes to the
Windows fleet, `image:` only picks the VM template it starts.

Also drops the hard-coded /c/Users/admin from the deploy job's cache
snapshot in favor of ${HOME}. The conan home is `~/.conan2`, so that
literal was an assumption about the old service account, and under any
other one the job shipped an empty artifact and stayed green -- exactly
the kind of assumption a move to a different machine has to stop making.

Docs: USAGE.md §5.6, §9.x, §10.2 and the troubleshooting list, including
two new entries for the failures this shape can produce.
GLR-UV carries VS2019 alongside VS2022, so the matrix that had to be
built by hand on a developer workstation can be built on every pipeline
instead. Opt-in: it roughly doubles the Windows half of a pipeline, and
only the libraries the VS2019-era desktop products consume need msvc 192
binaries at all.

The jobs are emitted beside the msvc 194 pair, not instead of it, and
reuse its shape -- same runner, same ABI fan-out, same tag-gated
export/restore split. Four things differ, and all four are load-bearing:

* `build.py --platform windows_vs2019` selects the matrix. The recipe
  already forks its third-party stack on compiler.version; what was not
  reachable from build.py is `apply_boost_defaults=False`, which the
  legacy boost/1.74.0.3 recipe needs because Conan fails a build outright
  when a profile sets an option no recipe in the graph defines. Both that
  and the upload destination are derived from --platform rather than
  exposed as separate flags: a pipeline cannot then set the matrix and
  the destination differently.
* The aquaveo-vs2019 remote is added with `xmsconan_conan_setup
  --remote-name ... --append`. The function already took both; only the
  CLI could not reach them. Appended, not inserted first, so it does not
  become the first stop for every conan install on a shared runner.
* Every deploy step carries --package-query compiler.version=192, which
  xmsconan_conan_deploy also gained. A runner's Conan cache is per
  machine, not per job, and both `conan cache save` and `conan upload`
  match by *reference* -- so an unqueried msvc 192 publish would carry a
  concurrent msvc 194 job's binaries onto the remote whose only purpose
  is to keep the two apart, and exit 0 having done it. `cache save`
  cannot take a query directly, so it resolves one through `conan list`
  into a package list, in a temp file deleted on both paths.
* No wheels. A wheel's tags say nothing about which MSVC built it, so an
  msvc 192 wheel and an msvc 194 wheel are the same devpi filename and
  publishing both would have them overwrite each other by upload order.
  The VS2019 wheels stay a deliberate manual step.

VS2019_PLATFORM_KEY moves to constants.py, where the generator and the
workstation driver both read it, and the generated build.py carries the
three msvc 192 literals rendered in rather than imported -- a new name in
xmsconan.constants would otherwise make a freshly generated build.py die
on an ImportError against an older installed client.

Docs: USAGE.md CLI table, [ci] table, build.py flag table, section 10.2,
section 16's premise (which said none of this runs in CI), the required
CI variables, and three troubleshooting entries.
@wdolinar

wdolinar commented Sep 1, 2026

Copy link
Copy Markdown
Member

Review of PR #129 (head dd01421, 14 files, +964/-62) — 19 findings after validation

Review: PR #129 — Move the GitLab Windows jobs to GLR-UV, and build msvc 192 in CI

Summary

What changed. 14 files, +964/-62, spanning three surfaces at once: the GitLab CI Jinja template (gitlab-ci.yml.jinja gains a Conan Build - Windows VS2019 / Conan Deploy - Windows VS2019 pair behind a new [ci].windows_vs2019 opt-in, and the existing Windows jobs move from GLR-${PY_TAG} to GLR-UV), the xmsconan_conan_setup / xmsconan_conan_deploy CLIs (new --remote-name / --remote-url / --append, --remote / --package-query), and the test suite. Because the change puts a second toolchain's binaries onto shared runners and shared Conan remotes, the load-bearing question throughout is which binaries end up on which remote.

Notably, the opt-in is not silently forgiving: xmsconan/generator_tools/ci_file_generator.py:363-369 rejects [ci].windows_vs2019 = true with [ci].windows = false at generation time, with an error naming both keys and both remedies, rather than emitting a pipeline that quietly resurrects the Windows half a repository asked not to have.

Validation. Seven specialists ran; three were skipped for having no matching files and two for an absent project marker. The 13 CRITICAL/MAJOR findings they raised went through an independent validation pass, and 11 were dropped. Several drops were not close calls — the finders' supporting evidence was factually wrong. The one CRITICAL (unpaired --remote-name / --remote-url) needs deliberate operator misuse against the flag's own help text and is unreachable from any caller in the tree. The _package_list "removed the :* guard" finding is wrong on its face: conan_deploy.py:44 still reads ["conan", "list", f"{ref}:*", "-c", "-p", package_query, "--format=json"]. The exact-assertion complaint at test_build_file_generator.py:759 had the file's convention backwards (79 exact-substring assertions, one regex exception). The duplicated-bootstrap finding claimed this diff "hand-edited all four sites"; the diff hand-edited two, the other two being brand-new jobs. Those findings do not stand and are not reported below. Two survived.

Verdict: REQUEST CHANGES (C=0 M=2 m=17)

Two specialists returned BLOCK. Both rested entirely on findings validation dropped — the type-design reviewer's on the dropped CRITICAL, and the writer-reviewer's on a missing DESIGN:/EVIDENCE: marker convention that a grep of the worktree shows this repository does not have (one hit, in an unrelated test docstring; CLAUDE.md's actual pre-merge checklist is satisfied). With no surviving basis, neither escalates the aggregate.

Major items

M1: README.md:383 — README still says msvc 192 cannot run in CI

## VS2019 (msvc 192) Packages

GitHub retired the `windows-2019` runner image, so the msvc 192 binaries the Aquaveo desktop products (GMS/SMS/WMS) consume are built **manually, on a developer workstation with Visual Studio 2019 installed**, and published to a separate Conan remote, `aquaveo-vs2019`.

**None of this runs in CI, by design.** CI is unchanged: it still builds gcc 13 / apple-clang 17 / msvc 194 and publishes to the `aquaveo` remote (the `aquaveo-stable` Artifactory repo). There is no Windows-2019 CI job to restore — the runner image is gone.

Change: Rewrite README.md:381-385 to mirror the passage this PR already added at docs/USAGE.md:881-887 ("GitLab CI can now build this matrix too… a repository can set [ci].windows_vs2019 = true"), and add [ci].windows_vs2019 to README's build.toml coverage. Three other in-tree passages state the same now-false thing and need the same treatment: xmsconan/build_tools/vs2019_build.py:3-7 ("can no longer be produced in CI", in a file this diff does edit), xmsconan/package_tools/packager.py:112-115, and xmsconan/constants.py:61-63 ("the manually built Visual Studio 2019 (msvc 192) packages"). Two corrections to the original finding, neither changing the outcome: constants.py was described as already-fixed but is unchanged by the diff and is a fourth stale location; and the sub-claim that README's Conan Setup / Conan Deploy sections omit the four new flags is true but weak, since README defers full flag reference to docs/USAGE.md.

Why: README.md:385 and docs/USAGE.md:883 now flatly contradict each other in the same tree, and README is the file a reader hits first — someone following it hand-builds msvc 192 on a workstation and never learns the opt-in exists. This is not pre-existing staleness; the PR is what made those sentences false, and CLAUDE.md's drift policy requires the update in the same PR.


M2: xmsconan/generator_tools/ci_templates/gitlab-ci.yml.jinja:718 — msvc 194 deploy/save carry no package query

{# :718 — "Conan Deploy - Windows" (msvc 194), no query #}
    - xmsconan_conan_deploy << library_name >> ${PACKAGE_VERSION} --restore .export/<< library_name >>-windows-py${PYTHON_TARGET_VERSION}-${PACKAGE_VERSION}.tar.gz --upload

{# :774 — "Conan Deploy - Windows VS2019" (msvc 192), queried #}
    - xmsconan_conan_deploy << library_name >> ${PACKAGE_VERSION} --restore .export/<< library_name >>-windows-vs2019-py${PYTHON_TARGET_VERSION}-${PACKAGE_VERSION}.tar.gz --upload --remote << vs2019_remote_name >> --package-query compiler.version=<< vs2019_msvc_version >>

Change: Add --package-query compiler.version=194 to both msvc 194 Windows steps — the save at :450 (which conan_deploy.py:96 turns into conan cache save --file X <ref>:*) and the upload at :718 (which conan_deploy.py:107 turns into conan upload <ref> -r aquaveo --confirm, no -p). The mechanism is already built: conan_deploy.py:80-89 resolves a query into a package list before saving, and :106-110 appends -p on upload.

Why: The template states the premise itself at :474-478 — "The Conan cache on a runner is per machine, not per job", so an unqueried conan cache save "would tarball the msvc 194 job's binaries". This PR mitigates that only in the 192→VS2019-remote direction and leaves the mirror open: the new VS2019 jobs sit on the same image: GLR-UV / tags: [WinVM] fleet, in the same stage, with no ordering, publishing the same <library>/<tag> reference — so <ref>:* on the msvc 194 side matches every package id, including the msvc 192 ones, and ships them to the production aquaveo remote at exit 0. Before this PR msvc 192 was workstation-only, so the hazard is created by the change. The validator's caveat: the leak pollutes the CI remote rather than breaking msvc 194 consumers, so MAJOR is defensible but arguably a notch high.

Minor items

  • tests/test_build_file_generator.py:775 — function-local import os/subprocess/sys shadow the module-level imports at :4, :7, :8; drop the local imports.
  • tests/test_build_file_generator.py:795Path(generator_tools.__file__).resolve().parents[2] resolves to site-packages under an installed layout, the exact case the comment at :792-794 says the PYTHONPATH pin prevents; derive the repo root from __file__.
  • tests/test_build_file_generator.py:796 — the suite's only unmocked subprocess.run carries no marker, though pyproject.toml:81 defines an integration marker; mark it or note why not.
  • tests/test_build_file_generator.py:804 — comment claims "every configuration in the table is msvc 192" but :805-806 are unanchored substring checks over free-form stdout; parse the compiler.version column.
  • tests/test_ci_file_generator.py:390if "install" in step is over-broad; a future conan install step gets swept in; filter on "pip install".
  • tests/test_ci_file_generator.py:1614 — six new VS2019 tests appended flat to a 181-def, ~2900-line module; group them under one class.
  • tests/test_ci_file_generator.py:1668 — nothing asserts the VS2019 build's --save name matches the deploy job's --restore, a pairing the suite enforces for Linux at :1516 because a mismatch fails only at tag time; add the agreement assertion.
  • tests/test_ci_file_generator.py:1702 — exact membership assert over a list already filtered by startswith; assert an entry without --remote-name instead.
  • tests/test_conan_deploy.py:100 — command verified by [:3]/[-3:] slices that skip the middle, so deleting the load-bearing -c still passes; assert the full command list.
  • tests/test_conan_deploy.py:122 — two near-identical side_effect recorders duplicated and coupled to argv position; extract one shared recorder helper.
  • xmsconan/ci_tools/conan_deploy.py:20_package_list returns a bare path with a docstring-only ownership contract; make it a contextlib.contextmanager.
  • xmsconan/ci_tools/conan_deploy.py:39 — temp file leaks on FileNotFoundError from :47 or an OSError from open() at :46, though the docstring at :34-37 promises "a failed resolve leaves nothing behind"; unlink in a finally.
  • xmsconan/ci_tools/conan_setup.py:7 — usage line has a run of spaces where a \ continuation belongs, unlike conan_deploy.py:8-9; add the backslash.
  • xmsconan/generator_tools/ci_file_generator.py:393 — a second if ci_type == "github": block 23 lines after the first at :370; merge the two.
  • xmsconan/generator_tools/ci_templates/gitlab-ci.yml.jinja:428 — two concurrent WinVM jobs now both mutate ~/.conan2/remotes.json; unresolved read-only, confirm the conan 2.31 RemoteRegistry save path locks that write.
  • xmsconan/generator_tools/templates/build.py.jinja:43--platform is a free str over a closed four-value set compared by exact equality at :70 and :147; add choices= from the rendered platform keys.
  • xmsconan/generator_tools/templates/build.py.jinja:56 — the generated __main__ block is now 98 lines with no seam between filtering, building, wheels, and upload; split into named steps.

@gagelarsen gagelarsen changed the title Move the GitLab Windows jobs to GLR-UV, and build msvc 192 in CI Move the GitLab Windows jobs onto the GLR-UV runner Sep 1, 2026
M2 was the real defect. The msvc 192 publishes were queried; the msvc 194
ones were not, and the hazard is symmetric. A runner's Conan cache is per
machine, and `conan cache save <ref>:*` / `conan upload <ref>` match by
reference, so once both toolchains build the same reference on the same
fleet, the unqueried side ships the other's binaries -- and that direction
is the worse one, because it puts msvc 192 binaries on the production
`aquaveo` remote at exit 0. Both Windows jobs now carry
`--package-query compiler.version=...`, unconditionally rather than only
under the opt-in: a guard present in just the configuration that needs it
is one refactor from being dropped from the one that does.

Both values are read from the packager's `configurations` rather than
written into the template. A stale literal would not fail loudly -- a
query naming a version the matrix no longer builds matches nothing, and
the job goes green having published no binaries at all.

M1: this PR made four in-tree passages false and only fixed one of them.
README's VS2019 section, `vs2019_build`'s module docstring, the
`windows_vs2019` comment in `packager.configurations`, and
`VS2019_REMOTE_NAME`'s in constants all still said msvc 192 cannot be
built in CI. README also gains the `[ci].windows_vs2019` row.

Minor items:

* `_package_list` is a context manager. The ownership contract lived in a
  docstring, and the cleanup was on `CalledProcessError` alone -- so a
  `conan` missing from PATH leaked the file the docstring promised would
  never be left behind. Now covered by a parametrized test for both
  failures.
* `--platform` takes `choices` from the packager's matrix, so a typo
  fails at parse time rather than after the boost-defaults and upload
  destination have been decided against a value matching nothing.
* The msvc 192 preview test reads the `compiler.version` column instead
  of searching stdout for "192"; the substring form also passed on an
  empty matrix, which is the failure most worth catching.
* The subprocess PYTHONPATH pin derives the repo root from the test
  file, not from an imported module's `__file__` -- which resolves into
  site-packages under an installed layout, defeating the pin.
* Whole-command assertions in the conan_deploy tests: the `[:3]`/`[-3:]`
  slices skipped `-c`, the flag that keeps the list a list of *cached*
  binaries. One shared recorder helper instead of two copies.
* A test that the VS2019 save and restore spell the same tarball, the
  pairing the Linux jobs already have, because a mismatch fails only at
  tag time.
* `"pip install"` rather than `"install"` when filtering steps; the
  un-flagged `conan_setup` call asserted by shape; the eleven msvc 192
  tests grouped under one class; the `\` continuation restored in
  conan_setup's usage docstring.

Investigated and documented rather than changed: conan 2.31 writes
`remotes.json` via a `.tmp` plus `os.replace`, so concurrent jobs cannot
tear the file, but nothing locks the read-modify-write and a simultaneous
add can lose one remote. The failure is loud and the fix -- a per-job
CONAN_HOME -- gives up the shared package cache these builds need, so it
is noted at both call sites and in the troubleshooting list instead.

Declined, with reasons: marking the preview test `integration` (that
marker means real conan + gcovr and multi-minute wall time; this runs no
conan and takes a second, and the file already has other unmocked
subprocess calls), merging the two `if ci_type == "github"` blocks (both
predate this PR -- master has the same four), and splitting the generated
build.py `__main__` (pre-existing shape this diff adds ~15 lines to;
restructuring the script every repo runs belongs in its own change).
@gagelarsen gagelarsen changed the title Move the GitLab Windows jobs onto the GLR-UV runner Move the GitLab Windows jobs to GLR-UV, and build msvc 192 in CI Sep 1, 2026
@gagelarsen

Copy link
Copy Markdown
Member Author

Thanks for this — M2 was a real defect and I would have shipped it. Addressed in 8779d95, on top of the branch you reviewed (dd01421), so the diff you already read still stands and this commit is only the response.

One thing to flag up front: between your review landing and my seeing it, I briefly force-pushed this branch down to just the uv commit while discussing whether the msvc 192 work was worth its complexity. The decision was to keep it, and dd01421 is restored unchanged — but that is why the PR timeline shows a force-push that dropped the commit your review targets. Nothing in your review was lost; line numbers below refer to the restored tree.

M2 — mirror leak, fixed

You were right that the mitigation was one-directional, and right that the open direction is the worse one: msvc 192 binaries onto the production aquaveo remote, at exit 0. Both Windows jobs now carry --package-query compiler.version=... on both the save and the upload.

Two decisions worth surfacing:

  • Unconditional, not gated on [ci].windows_vs2019. The restriction is a no-op when only one toolchain is on the machine, and a guard that appears only in the configuration that needs it is one refactor away from being dropped from the one that does.
  • Both values read from packager.configurations, not written into the template. A stale literal here would not fail loudly — conan upload -p compiler.version=194 after a bump to 195 matches nothing, so the job goes green having published no binaries at all. _only_msvc_version() also raises if a matrix ever pins more than one version, since "the version this job publishes" would then be a guess. Pinned by test_windows_package_query_tracks_the_packager_matrix.

M1 — stale docs, fixed

All four sites: README.md's VS2019 section, vs2019_build's module docstring, the windows_vs2019 comment in packager.configurations, and VS2019_REMOTE_NAME's in constants.py. README also gains the [ci].windows_vs2019 row.

Your correction about constants.py was right: I had rewritten MSVC_VS2019_VERSION's comment and missed the one directly above it. And I took your read on the sub-claim — README's Conan Setup / Deploy sections do not list the four new flags, but README defers flag reference to docs/USAGE.md, which has them, so I left those alone.

Minor items

Applied: the contextmanager for _package_list (which also fixed the leak you spotted at :39 — the cleanup was on CalledProcessError alone, so a conan missing from PATH leaked the file the docstring promised would never be left behind; both failures are parametrized tests now), choices= on --platform, the compiler.version column parse, the repo-root derivation, whole-command assertions, the shared recorder helper, the save/restore pairing test, the "pip install" filter, the un-flagged conan_setup assertion, the TestVs2019Ci grouping, and the continuation backslash.

Two of your minors were sharper than they looked:

  • The PYTHONPATH pin deriving from generator_tools.__file__ resolved into site-packages under an installed layout — it defeated the exact pin it was written to provide. It now comes from the test file's own location.
  • The preview assertions: beyond being unanchored, "194" not in stdout also passed on an empty matrix, which is the failure most worth catching in that test.

remotes.json — your open question, answered

Conan 2.31 writes it as remotes.json.tmp followed by os.replace (conan/api/subapi/remotes.py, _save), so the file cannot be torn — a reader sees one complete document or the other. But nothing locks the read-modify-write, so two jobs adding remotes in the same instant can lose one of the two additions.

Left as is, deliberately, and now documented at both call sites and in the troubleshooting list. The lost update is loud — the job that lost its remote fails resolving dependencies rather than publishing somewhere wrong — and the fix, a per-job CONAN_HOME, gives up the shared package cache these Windows builds depend on. That is a cache-model change with its own blast radius, not a footnote to this one. Happy to open it as a separate issue if you think the exposure warrants it; note it also predates this PR, since the three msvc 194 ABI legs already run xmsconan_conan_setup concurrently on one machine.

Three declined

  1. integration marker on the preview test. The marker is defined in pyproject.toml:81 as "real conan + gcovr … multi-minute wall time". This test runs no conan and takes about a second, so marking it would exclude it from the default run for no benefit. The supporting claim does not hold either — it is not the suite's only unmocked subprocess.run; test_build_file_generator.py has others at :1475 and :1792, equally unmarked.
  2. Merging the two if ci_type == "github": blocks. Both predate this PR — origin/master has the same four at :242, :351, :374, :544. This diff adds a nested if inside the existing one rather than a new block, so merging them is an unrelated refactor.
  3. Splitting the generated build.py __main__. Pre-existing shape that this diff adds ~15 lines to. Restructuring the script every downstream repo runs has its own blast radius and belongs in its own change.

Happy to be argued out of any of the three.

Verification

2351 passed, 4 skipped; flake8 clean; conan_deploy and conan_setup at 100% line and branch. All four Windows publish steps were checked in a rendered pipeline, with the opt-in both on and off.

@wdolinar
wdolinar merged commit 1d13779 into master Sep 2, 2026
8 checks passed
@wdolinar
wdolinar deleted the gitlab-uv-runner branch September 2, 2026 00:16
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.

2 participants