Skip to content

Replace the workflow_run chain with a single same-run build graph - #18

Merged
zeyuyang42 merged 3 commits into
mainfrom
ci-restructure
Aug 24, 2026
Merged

zeyuyang42 merged 3 commits into
mainfrom
ci-restructure

Conversation

@zeyuyang42

Copy link
Copy Markdown
Collaborator

Replaces the two chained build workflows with one ci.yml. Net −94 lines.

The actual bug in the old structure

The core and PureData workflows were chained with workflow_run. That trigger checks out the default branch, not the ref that triggered it, while downloading the core artifact from the triggering run. So dispatching the core build on core_modernization made the PureData workflow compile old main sources against a new libzerr_core.a:

undefined reference to `zerr::EnvelopeGenerator::EnvelopeGenerator(
    zerr::SystemConfigs, std::string, std::string)'

— because the constructor had become GenMode-typed. It looked like a real defect. It wasn't. Nothing about that failure was avoidable by configuration; it is what the trigger does.

In ci.yml every job is in one run and wrapper jobs take the core artifact from their own run — no run-id, no github-token, no "Ensure core artifact id present" guard. A cross-commit mix is no longer representable.

Artifacts keyed by toolchain, not platform

This is the part that matters for where the project is going. Windows needs two core builds — MinGW for PureData, and MSVC with static CRT for Max/MSP, since max-pretarget.cmake forces /MT. A single zerr-core-windows artifact cannot express that, so the old structure could never have reached Mac/Linux/Windows × PD/Max/JACK/SuperCollider.

id runner profile generator
macos macos-latest profiles/macos default
linux ubuntu-latest profiles/linux default
windows-mingw windows-latest profiles/mingw MinGW Makefiles
windows-msvc (follow-up) windows-latest profiles/windows-msvc Visual Studio

Adding a platform or compiler is one row here plus one row in the consumer job.

Max/MSP gets CI for the first time

On any platform. macOS only for now — Max doesn't exist on Linux (README already marks it ➖), and Windows needs the MSVC/static-CRT path above. Tracked as a follow-up.

Also fixed

  • The two workflows resolved dependencies differently on Windows. Core used inline -s compiler=gcc -s compiler.version=13 -s compiler.libcxx=libstdc++11; PureData used -pr:h=profiles/mingw, which also pins cppstd=17, threads=posix, exception=seh. All are package_id inputs, so the two jobs could resolve different binary packages of fftw/yaml-cpp for one logical build — PR Resolve conan dependencies once at the repo root #13's single-resolve invariant, broken at the CI layer. Both now take the profile from the same matrix row.
  • Composite setup-deps action replaces the conan block duplicated , and caches ~/.conan2. fftw and yaml-cpp currently build from source on macOS and Windows every run — most of the Windows job's 3m41s on Run the core build on pull requests #17.
  • Path filters dropped. A core change must rebuild every wrapper; a wrapper-only change must still build. The old filters are why a puredata/- or maxmsp/-only change triggered nothing at all.
  • concurrency group cancels superseded runs; checkout@v3@v4 (v3 targets deprecated Node 20).

deploy-doxygen.yml is untouched — it fails by design pending the org owner, per docs/design/github-pages-deployment.md.

Review notes

This PR validates itself. Thanks to #17 the checks run here, so what you see below is the verification. Worth watching:

  1. core ×3 → puredata ×3 + maxmsp ×1, wrappers starting only after core.
  2. puredata (windows-mingw) is the job that matters most — the only one that can catch core/wrapper API drift at build time. macOS links externals with -undefined suppress and Linux permits undefined symbols in a .so, so both would happily produce an external that fails at load.
  3. Known risk: the Windows core job now uses profiles/mingw, whose compiler_executables pins the x86_64-w64-mingw32-* names, and whose compiler.version=13 no longer matches the runners' gcc 16.1.0. The old inline settings sidestepped both. If this fails, the fix is to drop the compiler_executables pin or add a native profile — and we find out here rather than on main, which is exactly why Run the core build on pull requests #17 went first.

🤖 Generated with Claude Code

zeyuyang42 and others added 3 commits August 20, 2026 01:39
The core and PureData workflows were chained with `workflow_run`. That is not
merely awkward, it is wrong: `workflow_run` checks out the *default branch*
rather than the ref that triggered it, while downloading the core artifact from
the triggering run. Dispatching the core build on core_modernization therefore
made the PureData workflow compile old main sources against a new
libzerr_core.a, failing with

  undefined reference to zerr::EnvelopeGenerator::EnvelopeGenerator(
      zerr::SystemConfigs, std::string, std::string)

because the constructor had become GenMode-typed. That looked like a real defect
and was not.

ci.yml puts every job in one run. Wrapper jobs take the core artifact from their
own run -- no run-id, no github-token, no "Ensure core artifact id present"
guard -- so a cross-commit mix is not representable rather than merely unlikely.

Artifacts are keyed by TOOLCHAIN rather than by platform. Windows needs two
distinct core builds: MinGW for PureData, and MSVC with static CRT for Max/MSP,
whose max-pretarget.cmake forces /MT. A single zerr-core-windows artifact could
never express that, so the old structure could not reach the target matrix this
project is heading for (Mac/Linux/Windows x PD/Max/JACK/SuperCollider). Adding a
platform or a compiler is now one matrix row plus one row in the consumer.

Max/MSP gains its first CI job on any platform. macOS only for now -- Max does
not exist on Linux, and Windows needs the MSVC/static-CRT path above.

Also folded in:

- The Windows core build moves off its inline conan settings onto
  profiles/mingw. Those inline settings omitted compiler.cppstd,
  compiler.threads and compiler.exception, all of which are package_id inputs,
  so the core and PureData jobs could resolve *different binary packages* of
  fftw and yaml-cpp for one logical build -- the single-resolve invariant broken
  at the CI layer.
- A composite setup-deps action replaces the conan block that was duplicated 6
  times, and caches ~/.conan2. fftw and yaml-cpp currently build from source on
  macOS and Windows every run; the Windows job spent most of its 3m41s there.
- Path filters dropped. A core change must rebuild every wrapper and a
  wrapper-only change must still build; the old filters are why a puredata- or
  maxmsp-only change triggered nothing at all.
- concurrency group so superseded runs are cancelled, and checkout v3 -> v4
  (v3 targets the deprecated Node 20).

deploy-doxygen.yml is untouched: it fails by design pending the organization
owner's decision on Pages, and is documented in
docs/design/github-pages-deployment.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Seven fixes from review of the previous commit. The `arch` one had been wrong
since long before this branch.

Workflow:

- setup-mingw takes `platform`, not `arch`. Run 32314194864 logged "Unexpected
  input(s) 'arch', valid inputs are ['platform', 'cc', 'version']" in both
  windows-mingw jobs -- the x64 pin was silently discarded and the builds were
  64-bit only because that is the action's default. Carried over verbatim from
  the deleted workflows, so it had never worked.

- Wrapper jobs gain `if: !cancelled()`. `needs` cannot name a single matrix leg,
  so a macOS-only core failure used to *skip* every wrapper -- including
  puredata (windows-mingw), which the file itself identifies as the only job that
  can catch core/wrapper API drift at build time. Now the leg whose artifact is
  genuinely missing fails at download and the rest still report. The wait on the
  slowest core leg is inherent to matrix-level `needs` and is left as is, noted
  in a comment.

- if-no-files-found: error on all three uploads. The two leaf jobs had nothing
  downstream to notice an empty publish, so a wrapper that stopped emitting into
  puredata/build/zerr or maxmsp/externals would have stayed green.

- cancel-in-progress no longer applies to main. Two merges in quick succession
  would have cancelled the first one's run, leaving that commit without the
  artifact set the file's own header promises.

- Explicit `permissions: contents: read`. No cross-run artifact download remains,
  so no job needs a token beyond checkout.

setup-deps:

- ImageVersion added to the cache key. profiles/mingw pins compiler.version=13
  while the runners compile with gcc 16.1.0, so neither the package_id nor a
  conanfile/profile hash changes when the image bumps its toolchain: conan would
  have restored fftw and yaml-cpp built by the previous compiler and linked them
  into a core built by the new one -- exactly what the key's own comment claimed
  to prevent.

- restore-keys dropped. A prefix restore is by definition the stale hit that key
  exists to avoid, and it re-saved the restored tree under the new key, so
  ~/.conan2 would accrete every historical revision until it hit the 10GB
  repository ceiling and began evicting live entries.

Docs:

- dependency-fallbacks.md section 3.7 asserted that the core CI job deliberately
  bypasses profiles/mingw, and cited a workflow file this branch deletes. Both
  halves are now wrong: the core job resolves through the profile, and the pinned
  compiler_executables names verifiably work. Rewritten, with the surviving
  compiler.version drift and how the cache key compensates.

- repo-audit section 2.6 gets a status block, matching how 2.3 was handled, so
  the dated audit is not read as current state.

The arm64-only macOS artifacts are deliberately not addressed here. That is a
real design decision -- CI runners are arm64, so a universal binary needs either
-G Xcode or an explicit CMAKE_OSX_ARCHITECTURES -- and it is recorded against
section 2.5, which already flagged the same gap for build.sh.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit put ${{ env.ImageVersion }} in the cache key to make a runner
toolchain bump a cache miss. It did not work: the `env` context is not populated
with the runner image's variables inside a composite action's `with:` block, so
the expression expanded to an empty string. Run 32787801135 shows the result --
`conan-Windows--profiles/mingw-b46d38c5...`, with a blank field where the image
id should be. The key changed once, which forced a one-time miss, and then went
back to being blind to the compiler.

Read it in a shell step instead, where the variable is actually set, and pass it
through a step output. Falls back to "unknown" rather than empty so a self-hosted
runner without the variable still produces a well-formed key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@zeyuyang42
zeyuyang42 merged commit 87d6bf0 into main Aug 24, 2026
7 checks passed
@zeyuyang42
zeyuyang42 deleted the ci-restructure branch August 24, 2026 23:37
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.

1 participant