diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2f85105..ed3a521 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,7 +11,15 @@ jobs: name: Test & Build strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + # Keep this list up to date with supported Windows runner images. + # + # When windows-latest ships a new Visual Studio major version and + # test_newest_vs_known fails, follow the steps in MAINTENANCE.md: + # add the outgoing windows-latest image here (e.g., windows-2026) + # so its toolchain stays tested, then update the test and autobuild + # source for the new one. + # See: MAINTENANCE.md for other steps + os: [ubuntu-latest, macos-latest, windows-latest, windows-2022] python-version: ["3.11"] include: - os: ubuntu-latest diff --git a/MAINTENANCE.md b/MAINTENANCE.md new file mode 100644 index 0000000..4b56988 --- /dev/null +++ b/MAINTENANCE.md @@ -0,0 +1,87 @@ +# autobuild Maintenance Guide + +## Keeping Windows CI up to date with new Visual Studio releases + +### Background + +GitHub Actions `windows-latest` is a rolling label: when GitHub upgrades it to +a new Windows runner image, it ships a newer version of Visual Studio. autobuild +needs to know about each Visual Studio major version so it can map it to the +correct C++ toolset identifier (e.g. `v143` for VS 2022, `v145` for VS 2026). + +### Strategy + +1. **Sentinel in the test suite** — `tests/test_source_environment.py` defines + `NEWEST_KNOWN_VS_MAJOR`, the two-digit major version prefix of the newest + Visual Studio that autobuild currently supports (e.g. `"18"` for VS 2026). + The test `test_newest_vs_known` **fails** (rather than silently skipping) + whenever `windows-latest` ships an unrecognized major version. This is + intentional: a failure is a clear signal that action is required. + +2. **Pinned runners in `ci.yml`** — older Windows runner images (e.g. + `windows-2022`) are kept in the matrix so that every still-supported + toolchain is tested, even after `windows-latest` has moved on. + +3. **Retire unsupported runners** — when a Visual Studio version reaches + end-of-life and Linden no longer needs to support it, its pinned runner + can be removed from `ci.yml`. + +### What to do when `test_newest_vs_known` fails + +When `windows-latest` starts shipping a Visual Studio major version newer than +the current `NEWEST_KNOWN_VS_MAJOR`, CI will fail on that runner. Here is what +a developer needs to do to resolve it: + +#### Step 1 — Add the new toolset mapping to autobuild source + +Open `autobuild/autobuild_tool_source_environment.py` and find the `_VSTOOLSETS` +dictionary (search for `_VSTOOLSETS`). Add an entry for the new VS major version. +For example, if VS 2029 ships as major version `19`, its toolset identifier would +be `v150`: + +```python +_VSTOOLSETS = { + ... + "18": "v145", # 2026 + "19": "v150", # 2029 <-- add this +} +``` + +You may also need to add the same major version to the `AUTOBUILD_WIN_CMAKE_GEN` +mapping a little further down in the same file. Search for `"Visual Studio 18 2026"` +to find it. Add the new entry following the same pattern. + +#### Step 2 — Update `NEWEST_KNOWN_VS_MAJOR` in the test file + +Open `tests/test_source_environment.py` and update the `NEWEST_KNOWN_VS_MAJOR` +constant near the top of the file to match the new major version. The comment on +the same line should also reflect the new VS year. For example: + +```python +NEWEST_KNOWN_VS_MAJOR = "19" # Visual Studio 2029 +``` + +#### Step 3 — Pin the outgoing `windows-latest` image in `ci.yml` + +Open `.github/workflows/ci.yaml` and add the runner image that `windows-latest` +used to resolve to (e.g. `windows-2025`) to the `os` matrix list. This ensures +the old toolchain continues to be exercised in CI even though `windows-latest` +has moved on. + +```yaml +os: [ubuntu-latest, macos-latest, windows-latest, windows-2025, windows-2022] +``` + +#### Step 4 — Optionally retire old runner images + +Review the `os` matrix in `ci.yml` for any Windows runner images that target +Visual Studio versions Linden Lab no longer supports. Removing them reduces CI +run time. When in doubt, leave them in; it is easy to remove them later. + +### Files that need updating (summary) + +| File | What to update | +|---|---| +| `autobuild/autobuild_tool_source_environment.py` | `_VSTOOLSETS` dict, `AUTOBUILD_WIN_CMAKE_GEN` mapping | +| `tests/test_source_environment.py` | `NEWEST_KNOWN_VS_MAJOR` constant | +| `.github/workflows/ci.yaml` | `os` matrix — add pinned runner, optionally remove old ones | diff --git a/tests/test_source_environment.py b/tests/test_source_environment.py index d710462..b704ec1 100644 --- a/tests/test_source_environment.py +++ b/tests/test_source_environment.py @@ -11,6 +11,16 @@ from tests.basetest import * from tests.patch import patch +# The most recent Visual Studio major version (first two digits of AUTOBUILD_VSVER) +# that autobuild is known to support. This is used by test_newest_vs_known (below) +# to catch whenever windows-latest starts shipping an unrecognized compiler. +# +# When this test fails, follow the steps in MAINTENANCE.md to update autobuild +# for the new toolchain and move the old windows-latest image to ci.yml. +# +# See: MAINTENANCE.md +NEWEST_KNOWN_VS_MAJOR = "18" # Visual Studio 2026 + def assert_dict_subset(d, s): # Windows insists on capitalizing environment variables, so prepare a copy @@ -320,9 +330,38 @@ def test_config_shorthand(self): @needs_cygwin def test_vstoolset_set(self): - # n.b. This test will need to be updated from time to time: - # AUTOBUILD_VSVER is validated against the Visual Studio versions - # installed on the host system. - with envvar("AUTOBUILD_VSVER", "170"): + # Dynamically discover the latest installed Visual Studio version so + # this test keeps working when the runner image ships a newer VS. + available = atse._available_vsvers() + if not available: + self.skipTest("No Visual Studio install detected") + latest_vsver = available[-1] + major = latest_vsver[:-1] + expected_toolset = atse._VSTOOLSETS.get(major) + if expected_toolset is None: + self.skipTest("No toolset mapping for VS major version %s" % major) + with envvar("AUTOBUILD_VSVER", latest_vsver): vars = self.read_variables(self.find_data("empty")) - self.assertEqual(vars["AUTOBUILD_WIN_VSTOOLSET"], "v143") + self.assertEqual(vars["AUTOBUILD_WIN_VSTOOLSET"], expected_toolset) + + @needs_cygwin + def test_newest_vs_known(self): + # This test intentionally fails (rather than skips) when windows-latest + # ships a Visual Studio major version newer than NEWEST_KNOWN_VS_MAJOR. + # A failure here means autobuild needs to be updated to support the new + # toolchain. Follow the steps in MAINTENANCE.md to resolve it. + # + # See: MAINTENANCE.md + available = atse._available_vsvers() + if not available: + self.skipTest("No Visual Studio install detected") + latest_vsver = available[-1] + major = latest_vsver[:-1] + self.assertLessEqual( + major, + NEWEST_KNOWN_VS_MAJOR, + "Visual Studio major version %r is newer than the newest known version %r. " + "autobuild needs to be updated to support this toolchain. " + "See MAINTENANCE.md for the steps required." + % (major, NEWEST_KNOWN_VS_MAJOR), + )