From 325b99385550d1d7285f0976ee3b2ed084a9ecea Mon Sep 17 00:00:00 2001 From: HmUmaxKhan Date: Tue, 4 Aug 2026 07:02:23 +0500 Subject: [PATCH 1/3] Fix macOS CI by installing gcc for gfortran The macOS setup step only prepended $(brew --prefix gcc)/bin to PATH without installing the formula. brew --prefix exits 0 for any known formula whether or not it is installed, so the step passed while adding a non-existent directory, and meson then failed with "Unknown compiler(s)" when detecting Fortran. Install gcc (which provides gfortran) and verify it, so a missing toolchain fails loudly at setup instead of inside meson. Applied to build.yml, test.yml and release-pypi.yml -- the last of which would otherwise publish a release with no macOS wheels. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/build.yml | 7 +++++-- .github/workflows/release-pypi.yml | 7 +++++-- .github/workflows/test.yml | 7 +++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3b3d2f..dbcaa3c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,9 +42,12 @@ jobs: with: python-version: "3.12" - - name: Add gforran to PATH (macOS) + - name: Install gfortran (macOS) if: runner.os == 'macOS' - run: echo "$(brew --prefix gcc)/bin" >> "$GITHUB_PATH" + run: | + brew install gcc || brew link --overwrite gcc + echo "$(brew --prefix gcc)/bin" >> "$GITHUB_PATH" + "$(brew --prefix gcc)/bin/gfortran" --version - name: Install MinGW via Chocolatey (Windows) if: runner.os == 'Windows' diff --git a/.github/workflows/release-pypi.yml b/.github/workflows/release-pypi.yml index 9d0b04d..4199560 100644 --- a/.github/workflows/release-pypi.yml +++ b/.github/workflows/release-pypi.yml @@ -37,9 +37,12 @@ jobs: with: python-version: "3.12" - - name: Add gfortran to PATH (macOS) + - name: Install gfortran (macOS) if: runner.os == 'macOS' - run: echo "$(brew --prefix gcc)/bin" >> "$GITHUB_PATH" + run: | + brew install gcc || brew link --overwrite gcc + echo "$(brew --prefix gcc)/bin" >> "$GITHUB_PATH" + "$(brew --prefix gcc)/bin/gfortran" --version - name: Install Python dependencies run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 86fe98f..31bf252 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,9 +50,12 @@ jobs: with: fetch-depth: 0 - - name: Add gfortran to PATH (macOS) + - name: Install gfortran (macOS) if: runner.os == 'macOS' - run: echo "$(brew --prefix gcc)/bin" >> "$GITHUB_PATH" + run: | + brew install gcc || brew link --overwrite gcc + echo "$(brew --prefix gcc)/bin" >> "$GITHUB_PATH" + "$(brew --prefix gcc)/bin/gfortran" --version - name: Install MinGW via Chocolatey (Windows) if: runner.os == 'Windows' From 4149ed51a5125484b015317887488cb4b14e70b6 Mon Sep 17 00:00:00 2001 From: HmUmaxKhan Date: Tue, 4 Aug 2026 07:40:31 +0500 Subject: [PATCH 2/3] Pin macOS runners to macos-15 for wheel target macos-latest has rolled to macOS 26, so Homebrew serves gcc bottles whose libgfortran, libquadmath and libgcc_s carry minos 26.0. delocate vendors those into the wheel and then rejects them against MACOSX_DEPLOYMENT_TARGET=15.0, since the result would install on macOS 15 and fail at import. Pin the runner to macos-15 so the bottles match the wheel target. The image and MACOSX_DEPLOYMENT_TARGET must now be kept in sync. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/build.yml | 6 +++++- .github/workflows/release-pypi.yml | 6 +++++- .github/workflows/test.yml | 4 +++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dbcaa3c..64bc561 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,7 +29,11 @@ jobs: os: - ubuntu-latest - windows-latest - - macos-latest + # Pinned: Homebrew bottles on macos-latest (macOS 26) carry + # minos 26.0, which delocate rejects against the 15.0 wheel + # target below. Keep this image and MACOSX_DEPLOYMENT_TARGET + # in sync if either changes. + - macos-15 steps: - name: Checkout source diff --git a/.github/workflows/release-pypi.yml b/.github/workflows/release-pypi.yml index 4199560..e6d8741 100644 --- a/.github/workflows/release-pypi.yml +++ b/.github/workflows/release-pypi.yml @@ -23,7 +23,11 @@ jobs: os: - ubuntu-latest - windows-latest - - macos-latest + # Pinned: Homebrew bottles on macos-latest (macOS 26) carry + # minos 26.0, which delocate rejects against the 15.0 wheel + # target below. Keep this image and MACOSX_DEPLOYMENT_TARGET + # in sync if either changes. + - macos-15 steps: - name: Checkout source diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 31bf252..23c86b8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,9 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + # macos pinned to 15 to match the wheel target used in build.yml + # and release-pypi.yml; see the note there. + os: [ubuntu-latest, windows-latest, macos-15] python-version: ${{ fromJson(needs.fetch-python-versions.outputs.supported_versions) }} steps: From c96483666183ef42a2755d6e0f91e7ca4549972a Mon Sep 17 00:00:00 2001 From: HmUmaxKhan Date: Tue, 4 Aug 2026 07:48:32 +0500 Subject: [PATCH 3/3] Seed the banana tests to stop spurious CI failures test_banana_no_constraints called pso() with no seed while asserting convergence to within atol=0.001. PSO is stochastic, so the assertion fails on roughly 1.3% of runs (measured over 300 unseeded runs; worst deviation 1.77e-02, 17x the tolerance). Across a 15-job matrix that is about a 1-in-5 chance of a red run with nothing actually broken. Pin seed=2, which converges to within 1.4e-06 -- a 707x margin under the tolerance, so platform floating-point differences cannot cross it. Seed the constrained variant likewise; every other test in the suite already pins a seed. Co-Authored-By: Claude Opus 5 (1M context) --- tests/test_banana_no_constraints.py | 4 +++- tests/test_banana_with_constraints.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_banana_no_constraints.py b/tests/test_banana_no_constraints.py index 2bd3983..5e8f4c4 100644 --- a/tests/test_banana_no_constraints.py +++ b/tests/test_banana_no_constraints.py @@ -13,7 +13,9 @@ def banana_func(x: NDArray[np.floating]) -> np.floating: def test_banana_no_constraints() -> None: lb = [-3, -1] ub = [2, 6] - result = pso(banana_func, lb, ub, debug=True) + # Seeded: PSO is stochastic, and unseeded this assertion fails on + # roughly 1 in 75 runs at atol=0.001. + result = pso(banana_func, lb, ub, debug=True, seed=2) xopt, fopt = result.x, result.fun if not np.allclose(xopt, [1.0, 1.0], atol=0.001): msg = f"Expected xopt close to [1.0, 1.0], got {xopt}" diff --git a/tests/test_banana_with_constraints.py b/tests/test_banana_with_constraints.py index 9122b17..5cd2f26 100644 --- a/tests/test_banana_with_constraints.py +++ b/tests/test_banana_with_constraints.py @@ -19,7 +19,9 @@ def banana_constraint(x: NDArray[np.floating]) -> list[float]: def test_banana_with_constraints() -> None: lb = [-3, -1] ub = [2, 6] - result = pso(banana_func, lb, ub, f_ieqcons=banana_constraint, debug=True) + result = pso( + banana_func, lb, ub, f_ieqcons=banana_constraint, debug=True, seed=2 + ) xopt, fopt = result.x, result.fun if not np.allclose(xopt, [0.5, 0.75], atol=0.1): msg = f"Expected xopt close to [0.5, 0.75], got {xopt}"