Skip to content

ci(pip-build): respect disable-build-* labels for ubuntu / macos#6091

Open
Fedr wants to merge 10 commits into
masterfrom
ci/test-pip-build-respects-disable-labels
Open

ci(pip-build): respect disable-build-* labels for ubuntu / macos#6091
Fedr wants to merge 10 commits into
masterfrom
ci/test-pip-build-respects-disable-labels

Conversation

@Fedr
Copy link
Copy Markdown
Contributor

@Fedr Fedr commented May 13, 2026

Summary

test-pip-build (the workflow_call into pip-build.yml) ignored the disable-build-<platform> PR labels and always tried to run the full manylinux + macOS matrix. That bit #6090: a disable-build-linux-vcpkg label suppressed the corresponding prepare-image step, but test-pip-build still tried to docker pull meshlib/meshlib-rockylinux8-vcpkg-x64:<branch> and failed with manifest unknown.

Plumb three new boolean inputs through pip-build.yml and gate the matching build / test jobs so the same labels that skip the rest of CI also skip the corresponding pip-build legs.

Changes

  • .github/workflows/pip-build.yml:
    • New workflow_call inputs disable_ubuntu_x64, disable_ubuntu_arm64, disable_macos (all default false, so workflow_dispatch / release triggers keep the full matrix).
    • setup job gains two new outputs (build-matrix, test-matrix) populated by a single bash + jq step:
      • PLATS enumerates per-platform combos (the "include" part: runner / os-vcpkg / container-options / compiler / container-prefix).
      • The jq filter drops disabled platforms (the "exclude" part).
      • For the test matrix, the surviving platforms are cross-producted with the supported Python versions.
    • manylinux-pip-build / manylinux-pip-test: consume the prebuilt matrices via matrix: { include: ${{ fromJSON(needs.setup.outputs.<matrix>) }} }. Job bodies reference plain ${{ matrix.runner }} / ${{ matrix.os }} / ${{ matrix.compiler }} / ${{ matrix.container-prefix }} — the master-equivalent shape, no ternaries, no synthetic axis. Each job has if: needs.setup.outputs.<matrix> != '[]' so the both-disabled case skips cleanly rather than erroring on an empty matrix.
    • macos-pip-build / macos-pip-test: single job-level if: ${{ inputs.disable_macos != true }} (both x86 and arm64 macOS variants together — only one disable-build-macos label exists).
  • .github/workflows/build-test-distribute.yml: derive the three flags from the same build_enable_<platform> config outputs that the adjacent prepare-image already consumes, and pass them into the test-pip-build invocation.

Out of scope

  • disable-build-windowswindows-pip-build keeps running.
  • disable-build-emscriptenpip-build.yml has no emscripten target.

Why two-stage matrix instead of matrix.exclude with synthetic axis

A simpler strategy.matrix.exclude approach (synthetic disable_ubuntu_* axis carrying the input, exclude rule keyed on it — the same pattern used in build-test-linux-vcpkg.yml) was tried first but ran into the include-orphan trap: GitHub processes matrix in the order base → exclude → include. When exclude wiped all platform=aarch64 base rows, the per-platform {platform: "aarch64", runner: ..., ...} include entry had no surviving base to extend and was spawned as a fresh combo that bypassed exclude — observed as a (aarch64, ubuntu-24.04-arm, , arm64v8/) job running with an empty os / missing py-version that tried to docker pull arm64v8/. (The build-test-linux-vcpkg.yml pattern doesn't hit this because its excludes only remove specific (compiler, config) tuples, never the whole arch axis, so its per-compiler / per-arch includes always find a surviving combo to extend.)

The two-stage approach sidesteps the orphan entirely: there is no base × include split — every combo comes in via include directly, and filtering happens in jq.

Test plan

PR labels enable test-pip-build and selectively disable platforms (disable-build-{macos,ubuntu-arm64,linux-vcpkg,emscripten,windows}) to exercise the new gating end to end. Latest run:

  • setup runs and emits filtered matrices.
  • manylinux-pip-build (x86_64, ...) runs and passes — single combo, no aarch64.
  • manylinux-pip-test (x86_64, ..., 3.8 … 3.14) runs and passes for all 7 py-versions — no orphan combo this time.
  • aarch64 build / test jobs do not appear at all (filtered out by jq).
  • macos-pip-build / macos-pip-test skip cleanly (validates the macos gate).
  • windows-pip-build + 7 × windows-pip-test (3.x) run and pass (validates the windows path is untouched).
  • No regression in the publish-on-release path: the three inputs default to false, so workflow_dispatch / release triggers keep the full matrix.

`test-pip-build` (the workflow_call into pip-build.yml) ignored the
`disable-build-<platform>` PR labels and always tried to run the full
manylinux + macOS matrix. That bit PR #6090: a `disable-build-linux-vcpkg`
label suppressed the prepare-image step, but `test-pip-build` still tried to
pull the branch-tagged Rocky Linux image and failed with `manifest unknown`.

Plumb three new boolean inputs through pip-build.yml --
`disable_ubuntu_x64`, `disable_ubuntu_arm64`, `disable_macos` -- and gate
the matching build/test jobs:

- manylinux-pip-build / manylinux-pip-test: per-platform `if:` keyed on
  `matrix.platform` so x86_64 and aarch64 can be disabled independently
  (matches the `disable-build-ubuntu-{x64,arm64}` split elsewhere).
- macos-pip-build / macos-pip-test: single `if:` gated on `disable_macos`
  (both x86 and arm64 macOS variants together, matching the single
  `disable-build-macos` label).

`build-test-distribute.yml` derives the three flags from the same
`build_enable_<platform>` outputs that `prepare-image` already uses on the
adjacent line.

Out of scope (not requested): `disable-build-windows` (windows-pip-build
keeps running) and `disable-build-emscripten` (pip-build.yml has no
emscripten target).
Fedr added 3 commits May 13, 2026 15:45
The previous attempt used a job-level `if:` referencing `matrix.platform`,
but GitHub Actions rejects that for reusable-workflow jobs:

  Unrecognized named-value: 'matrix'.
  Located at position 2 within expression:
  (matrix.platform == 'x86_64' && inputs.disable_ubuntu_x64 != true) || ...

(The contexts table at https://docs.github.com/en/actions/learn-github-actions/contexts
lists `matrix` as available in `jobs.<job_id>.if`, but the parser disagrees
for reusable workflows. The workflow file failed to load entirely; no jobs
even started.)

Switch the per-platform gating to `strategy.matrix.exclude` with input-driven
expressions. When `inputs.disable_ubuntu_x64` is true, the exclude entry
matches and removes the `x86_64` row from the matrix; when it's false, the
entry resolves to the sentinel `__never__`, which doesn't match any real
matrix platform, so nothing is excluded. Same shape for `aarch64`.

Applied to both `manylinux-pip-build` and `manylinux-pip-test`. The macOS
gate (`if: ${{ inputs.disable_macos != true }}`) only references `inputs`
and was not affected by the parse error.
The previous attempt's `strategy.matrix.exclude` correctly removed the
`platform=aarch64` base combos when `inputs.disable_ubuntu_arm64` was set,
but a stray `(aarch64, ubuntu-24.04-arm, , arm64v8/)` test job still ran
with an empty `os`/`py-version`, and the `aarch64` build still ran.

Cause: GitHub processes matrix in the order base -> exclude -> include
(https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#expanding-or-adding-matrix-configurations).
A per-platform include entry (`{platform: "aarch64", runner: ..., ...}`)
that has no surviving base combo to extend is treated as a brand-new
combo by the include step. That orphan never gets re-filtered by exclude.

Fix: remove the per-platform include entries entirely and derive
`runs-on`, `container.image`, `container.options`, and the compiler /
container-prefix values directly from `matrix.platform` inline. The
matrix axis stays `platform: ["x86_64", "aarch64"]`, so exclude removes
the row cleanly without leaving an include orphan.

The `manylinux-pip-test` py-version include entries are left in place:
each has a `py-version` key that matches a real base axis value, so they
extend existing combos rather than create new ones, no orphan risk.

The compiler was `/usr/bin/clang++` for both platforms, so hardcoding it
in the build/bindings steps is a no-op behavior-wise.
The container.image expression on `manylinux-pip-test` was:

  ${{ matrix.platform == 'x86_64' && '' || 'arm64v8/' }}${{ matrix.os }}

That looks like a ternary picking `''` for x86_64 and `arm64v8/` for
aarch64, but GitHub Actions treats `''` as falsy, so for x86_64 it
evaluates `true && ''` -> `''`, then `'' || 'arm64v8/'` -> `'arm64v8/'`.
Result: x86_64 test runs tried to `docker pull arm64v8/rockylinux:8`
and failed with `no matching manifest for linux/amd64`.

Pick the full image name on each side so both operands of `&&` stay
truthy:

  ${{ matrix.platform == 'x86_64' && matrix.os || format('arm64v8/{0}', matrix.os) }}

The build-job image expression was unaffected because both literals
(`x64` / `arm64`) are non-empty. The `container.options` expression is
also unaffected because the aarch64 fallback is `' '` (a single space,
truthy in GHA) rather than empty.
Comment on lines +321 to +322
# Honor `disable-build-<platform>` PR labels so test-pip-build skips the
# same platforms as the rest of CI.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excess comment.

Comment thread .github/workflows/pip-build.yml Outdated
# value, so the exclude has no effect when the input is false.
# Job-level `if:` can't reference `matrix.*` in a reusable workflow,
# hence the exclude-based approach.
exclude:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check the build-test-linux-vcpkg workflow on how to exclude matrix configurations based on workflow inputs.

Comment thread .github/workflows/pip-build.yml Outdated
os: "ubuntu:25.10"
# Mirror the exclude on `manylinux-pip-build` so tests skip the same
# platforms. See the note there for the `__never__` sentinel.
exclude:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check the build-test-linux-vcpkg workflow on how to exclude matrix configurations based on workflow inputs.

Fedr added 4 commits May 14, 2026 12:25
Address review on #6091:

- Drop the redundant `# Honor disable-build-<platform>` comment above the
  three `disable_*` inputs in build-test-distribute.yml.
- Replace the `__never__`-sentinel `exclude:` on both manylinux-pip-build
  and manylinux-pip-test with the synthetic-axis pattern used in
  build-test-linux-vcpkg.yml: a one-element matrix axis carrying the
  `disable_*` input value, and an exclude rule that constrains on
  `{disable_<platform>: true, platform: <name>}`. When the input is false
  the rule never matches; when true it drops the matching platform leg.
…axis exclude

Per review on #6091: keep the original matrix shape from master (base
`platform: [...]` plus per-platform `include` entries feeding runner /
os / container-options / container-prefix / compiler) and layer only the
synthetic-axis `exclude:` from build-test-linux-vcpkg.yml on top.

Reverts the inline `${{ matrix.platform == 'x86_64' && ... || ... }}`
expressions for runs-on / container.image / container.options /
CMAKE_CXX_COMPILER / CXX_FOR_ABI / CXX_FOR_BINDINGS that I had
introduced when trying to avoid the post-exclude include orphan.
Drop the per-platform `include` entries on manylinux-pip-build and
manylinux-pip-test and compute `runs-on`, `container.image`,
`container.options`, and the `arm64v8/` prefix inline from
`matrix.platform`. The compiler (`/usr/bin/clang++`) is the same on both
platforms and is now lifted to a job-level `env.COMPILER` so the build
steps reference it once instead of repeating the literal.

The per-platform include was the source of the orphan combo that the
synthetic-axis exclude couldn't filter (GitHub processes matrix as
base -> exclude -> include, so an include constrained on an already-
excluded platform value spawns a fresh combo that bypasses exclude --
seen as `(aarch64, ubuntu-24.04-arm, , arm64v8/)` running with an empty
`os` and missing `py-version`).

The py-version include entries on manylinux-pip-test stay -- each has a
`py-version` key matching a real base axis value, so they extend
surviving combos rather than orphan.

Both inline conditional branches are non-empty (`format('arm64v8/{0}',
matrix.os)` rather than the literal `''` prefix) because GitHub Actions
treats empty string as falsy in `<cond> && X || Y`, which would
otherwise flip the prefix back to `arm64v8/` on x86_64.
Replace the inline `${{ matrix.platform == 'x86_64' && ... || ... }}`
ternaries on `manylinux-pip-build` / `manylinux-pip-test` with fully
data-driven matrices generated in the existing `setup` job.

The `setup` job now emits two new outputs:

- `build-matrix`: per-platform combos, with disabled platforms filtered
  out.
- `test-matrix`: cartesian product (surviving platforms x supported
  Python versions), again with disabled platforms filtered out.

The filter happens in a single bash + jq step. The consumer jobs become
trivial:

  matrix:
    include: ${{ fromJSON(needs.setup.outputs.build-matrix) }}

…and their bodies reference plain `${{ matrix.runner }}`, `${{ matrix.os
}}`, `${{ matrix.compiler }}`, `${{ matrix.container-prefix }}` -- the
master-equivalent shape. No synthetic axis, no `__never__` sentinel, no
`include` orphan can be created because there's no base x include split.

Each consumer job is gated by `if: needs.setup.outputs.<matrix> != '[]'`
so the whole job is skipped (rather than erroring on an empty matrix)
when both platforms are disabled.
Comment thread .github/workflows/pip-build.yml Outdated
]'

# Drop disabled platforms.
KEEP=$(jq -nc --argjson plats "$PLATS" --argjson dx "$DISABLE_X64" --argjson da "$DISABLE_ARM64" \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make the jq calls multi-line for better readability.

Per review on #6091: reformat the three `jq` calls in setup's matrix
step so each argument is on its own line and the filter program reads
top-to-bottom. Behavior is unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test-pip-build Build Python wheels (and discard them)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants