diff --git a/docs/USAGE.md b/docs/USAGE.md index 04b2865..1d1783d 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -515,7 +515,7 @@ The generated jobs follow the pattern: 1. **Setup Python + Conan** (`xmsconan_conan_setup --remote-url … --login`) 2. **Generate build files** (`xmsconan_gen --version …`) -3. **Build** (`python build.py --filter='{"build_type": ""}' --wheel-dir wheelhouse --artifacts-dir test_artifacts`) +3. **Build** (`python build.py --filter='{"build_type": ""}' --artifacts-dir test_artifacts`, plus `--wheel-dir wheelhouse` on Release — §10.1) 4. **Repair wheel** on Release (`xmsconan_wheel_repair --wheel-dir wheelhouse`) 5. **On tag pushes:** `xmsconan_wheel_deploy` and `xmsconan_conan_deploy … --upload` @@ -526,6 +526,7 @@ The generated jobs follow the pattern: - Job `name:` carries the version on any platform that fans out (`GCC-13 (Release, 3.14, Linux)`). GitHub uses an explicit `name:` verbatim and only auto-appends matrix values when none is given, so without this two legs would share one status-check name — ambiguous in the checks list and in branch-protection matching. A single-version platform keeps its original name, so existing required checks keep matching. - **Windows** matrix: `build_type × compiler-version × python-version=ci_python_versions`. - Wheel artifacts carry `-py${{ matrix.python-version }}` on any platform that fans out; a single-version platform keeps its bare `wheel-${{ runner.os }}` name. +- **`--wheel-dir` is passed on the Release leg only.** `[matrix].pybind_build_types` defaults to `["Release"]` (§5.4.1), so a Debug leg builds no pybind package — and `build.py` treats `--wheel-dir` as a request it must satisfy, exiting 1 when no complete set of wheels comes out (§9.1). Passing it on every leg therefore failed every Debug leg; it did exactly that in xmsconan 2.22.0–2.22.2, so a repo generated by one of those needs `xmsconan ci` re-run. Repair, artifact upload and deploy are already gated the same way, so a library that also names `Debug` loses no wheel it was publishing. - Linux containers resolve to `conan-gcc13-py${{ matrix.python-version }}:latest`. - `flake` deliberately stays on a single hardcoded interpreter — linting is ABI-independent, and pinning it keeps lint results identical across repos. - Third-party actions are referenced by **commit SHA**, with the tag in a trailing comment (`uses: nelonoel/branch-name@1ea5c86… # v1.0.1`). A tag is a movable ref in a repository Aquaveo does not control, and the job it runs in holds the Conan and devpi secrets. `actions/*` stays on tags — a compromise of GitHub's own namespace is a compromise of the runner regardless. To move a pin, resolve the new tag with `gh api repos///commits/ --jq .sha` and edit the **template**. `test_github_ci_pins_third_party_actions_to_a_sha` fails on any third-party action added by tag. diff --git a/tests/test_ci_file_generator.py b/tests/test_ci_file_generator.py index 93987da..1f540d7 100644 --- a/tests/test_ci_file_generator.py +++ b/tests/test_ci_file_generator.py @@ -530,6 +530,55 @@ def test_ci_pins_conan_version(ci_toml, tmp_path): assert [line for line in conan_installs if '"conan~=' not in line] == [] +def _build_step_run(job): + """The ``run:`` of one job's "Build the Conan Packages" step.""" + for step in job["steps"]: + if step.get("name") == "Build the Conan Packages": + return step["run"] + raise AssertionError("job has no 'Build the Conan Packages' step") + + +@pytest.mark.parametrize("job_name", ["mac", "linux", "windows"]) +def test_github_build_step_requests_a_wheel_only_on_release(ci_toml, tmp_path, job_name): + """``--wheel-dir`` reaches build.py on the Release leg alone. + + ``[matrix].pybind_build_types`` defaults to Release, so a Debug leg builds + no pybind package -- and build.py treats ``--wheel-dir`` as a request it + must satisfy, exiting 1 when no complete set of wheels comes out. Passing + the flag unconditionally therefore failed every Debug leg of every + consumer, which is what it did between xmsconan 2.22.0 and this fix. + + Asserted per job rather than over the whole document so a regression names + the platform that lost the gate. + """ + run = _build_step_run(_github_jobs(ci_toml, tmp_path)[job_name]) + + assert run.count("--wheel-dir") == 1 + assert "${{ matrix.build_type == 'Release' && '--wheel-dir wheelhouse" in run + + +def test_github_windows_skip_dependency_libs_stays_inside_the_wheel_gate(tmp_path): + """The unrepaired-Windows companion flag is gated with ``--wheel-dir``. + + ``--skip-dependency-libs`` is read only inside build.py's ``if + args.wheel_dir`` branch, so leaving it outside the gate would put a flag on + the Debug command line that reads as staging control and does nothing. + """ + toml_file = tmp_path / "build.toml" + toml_file.write_text( + 'library_name = "xmscore"\ndescription = "Core library"\nci_type = "github"\n' + "\n[ci]\nwindows_wheel_repair = false\n", + encoding="utf-8", + ) + + run = _build_step_run(_github_jobs(toml_file, tmp_path)["windows"]) + + assert ( + "${{ matrix.build_type == 'Release' && " + "'--wheel-dir wheelhouse --skip-dependency-libs' || '' }}" in run + ) + + def test_github_ci_includes_artifacts_dir_flag(ci_toml, tmp_path): """Rendered GitHub CI build commands include --artifacts-dir test_artifacts.""" output_dir = tmp_path / "output" diff --git a/xmsconan/generator_tools/ci_templates/github-ci.yaml.jinja b/xmsconan/generator_tools/ci_templates/github-ci.yaml.jinja index 38d781b..7793428 100644 --- a/xmsconan/generator_tools/ci_templates/github-ci.yaml.jinja +++ b/xmsconan/generator_tools/ci_templates/github-ci.yaml.jinja @@ -13,6 +13,13 @@ # (`gh api repos///commits/ --jq .sha`) and editing the # template -- not the generated file. # +# Every wheel step -- `--wheel-dir` on the build, then repair, artifact +# upload and deploy -- runs on the Release leg only. +# `[matrix].pybind_build_types` defaults to Release alone, so a Debug leg +# builds no pybind package, and build.py treats `--wheel-dir` as a request +# it must satisfy: it exits 1 when no complete set of wheels comes out. +# Asking a Debug leg for a wheel fails the leg instead of producing one. +# # Generated by xmsconan_ci — do not edit manually. name: << display_name >>-CI @@ -159,7 +166,7 @@ jobs: run: xmsconan_gen --version ${{ env.XMS_VERSION }} build.toml # Build the Conan Package - name: Build the Conan Packages - run: "python build.py --filter=\"{\\\"build_type\\\": \\\"${{ matrix.build_type }}\\\"}\" --wheel-dir wheelhouse --artifacts-dir test_artifacts" + run: "python build.py --filter=\"{\\\"build_type\\\": \\\"${{ matrix.build_type }}\\\"}\" ${{ matrix.build_type == 'Release' && '--wheel-dir wheelhouse' || '' }} --artifacts-dir test_artifacts" shell: bash - name: Upload test artifacts uses: actions/upload-artifact@v4 @@ -302,7 +309,7 @@ jobs: run: xmsconan_gen --version ${{ env.XMS_VERSION }} build.toml # Build the Conan Package - name: Build the Conan Packages - run: "python build.py --filter=\"{\\\"build_type\\\": \\\"${{ matrix.build_type }}\\\"}\" --wheel-dir wheelhouse --artifacts-dir test_artifacts" + run: "python build.py --filter=\"{\\\"build_type\\\": \\\"${{ matrix.build_type }}\\\"}\" ${{ matrix.build_type == 'Release' && '--wheel-dir wheelhouse' || '' }} --artifacts-dir test_artifacts" shell: bash - name: Upload test artifacts uses: actions/upload-artifact@v4 @@ -446,7 +453,7 @@ jobs: run: xmsconan_gen --version ${{ env.XMS_VERSION }} build.toml # Build the Conan Package - name: Build the Conan Packages - run: "python build.py --filter=\"{\\\"build_type\\\": \\\"${{ matrix.build_type }}\\\"}\" --wheel-dir wheelhouse --artifacts-dir test_artifacts" + run: "python build.py --filter=\"{\\\"build_type\\\": \\\"${{ matrix.build_type }}\\\"}\" ${{ matrix.build_type == 'Release' && '--wheel-dir wheelhouse' || '' }} --artifacts-dir test_artifacts" shell: bash - name: Upload test artifacts uses: actions/upload-artifact@v4 @@ -598,7 +605,7 @@ jobs: run: xmsconan_gen --version ${{ env.XMS_VERSION }} build.toml # Build the Conan Package - name: Build the Conan Packages - run: "python build.py --filter=\"{\\\"build_type\\\": \\\"${{ matrix.build_type }}\\\"}\" --wheel-dir wheelhouse<% if not ci_windows_wheel_repair %> --skip-dependency-libs<% endif %> --artifacts-dir test_artifacts" + run: "python build.py --filter=\"{\\\"build_type\\\": \\\"${{ matrix.build_type }}\\\"}\" ${{ matrix.build_type == 'Release' && '--wheel-dir wheelhouse<% if not ci_windows_wheel_repair %> --skip-dependency-libs<% endif %>' || '' }} --artifacts-dir test_artifacts" shell: cmd - name: Upload test artifacts uses: actions/upload-artifact@v4