Skip to content

ci(release): publish Conan binaries for Linux, macOS, and Windows#108

Merged
facontidavide merged 2 commits into
mainfrom
ci/release-multiplatform
May 29, 2026
Merged

ci(release): publish Conan binaries for Linux, macOS, and Windows#108
facontidavide merged 2 commits into
mainfrom
ci/release-multiplatform

Conversation

@facontidavide
Copy link
Copy Markdown
Contributor

Problem

The Release workflow ran on a single ubuntu-22.04 job, so Cloudsmith only ever received a Linux/gcc binary of plotjuggler_core. macOS and Windows consumers were therefore forced to build core from source via --build=missing.

That is slow, and on macOS it is brittle: a from-source build depends on the recipe's exported sources being intact in the local cache. After a version is re-published under a new recipe revision (or a cache is restored from a superseded one), the build dies with:

ERROR: The 'plotjuggler_core/0.5.0' package has 'exports_sources' but sources not found in local cache.
Probably it was installed from a remote that is no longer available.

This is exactly what broke the downstream pj-official-plugins macOS CI on main. (Earlier, pre-fast_float, the from-source macOS build also failed to compile because libc++ lacks std::from_chars for floating-point — now fixed in number_parse.hpp, but the from-source path remains an avoidable liability.)

Fix

Restructure the workflow into three jobs:

  • prepare — resolve the version/tag once and verify it matches conanfile.py (unchanged logic, now exposed as job outputs).
  • build — matrix over [ubuntu-22.04, macos-15-intel, windows-latest], each running conan create + conan upload with the same -s build_type=Release -s compiler.cppstd=20 settings consumers use, so the published package_id matches what downstream conan install resolves on each platform.
  • github-release — cut the GitHub Release once, gated on all platform binaries publishing successfully.

With a binary per platform, downstream macOS/Windows CI downloads core instead of compiling it — eliminating the from-source fragility entirely (and speeding those jobs up).

Notes

  • fail-fast: false so a flaky upload on one OS still publishes the others; re-run the failed leg via workflow_dispatch. The GitHub Release stays gated on every leg succeeding, so an incomplete release is never advertised.
  • Matrix legs each upload the recipe + their own binary; the recipe revision is identical across platforms, so concurrent recipe uploads are idempotent (only the per-platform binary differs).
  • package_id matching assumes the release runner and the consumer runner share a compiler version (e.g. both macos-15-intel). If a runner image bumps Xcode/MSVC between release and consumption, consumers harmlessly fall back to today's from-source behavior.

Follow-up (not in this PR)

The original incident's trigger was a moved tag (v0.5.0 published twice from two different commits). This PR makes consumers robust to that, but does not prevent it. A guard in prepare that refuses to publish over an existing-but-different recipe revision could be added separately — happy to include it if wanted.

🤖 Generated with Claude Code

The release job ran only on ubuntu-22.04, so Cloudsmith carried a single
Linux/gcc binary. macOS and Windows consumers therefore had to build
plotjuggler_core from source via --build=missing — slow, and on macOS
brittle: from-source builds depend on the recipe's exported sources being
intact in the cache, which breaks (e.g. after a version is re-published
under a new recipe revision) with "exports_sources but sources not found
in local cache".

Restructure into prepare -> build (matrix) -> github-release:
- prepare resolves the version/tag once and verifies it matches conanfile.py
- build fans out over [ubuntu-22.04, macos-15-intel, windows-latest],
  each running conan create + conan upload with the same Release/cppstd=20
  settings consumers use, so the published package_id matches downstream
- github-release cuts the GitHub Release once, gated on all platforms

With a binary per platform, downstream macOS/Windows CI downloads instead
of compiling, eliminating the from-source fragility entirely.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a prepare-job guard that refuses to publish plotjuggler_core/<version>
when it is already on Cloudsmith under a *different* recipe revision — the
exact footgun that broke macOS CI (v0.5.0 was published twice from two
different commits, replacing the recipe revision mid-resolve). It compares
the recipe revision this release would publish against the remote:
  - not published          -> proceed (first release)
  - same revision present  -> proceed (idempotent re-run after a partial upload)
  - a different revision    -> fail
Override with the workflow_dispatch `allow_republish` input to repair a
botched release.

Also force an LF checkout in the build matrix: with no .gitattributes,
Windows would otherwise check out CRLF and compute a different recipe
revision than Linux/macOS, splitting one version across two revisions and
defeating the matrix. LF everywhere keeps the revision identical across all
legs and matching the one the guard validates on Linux.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@facontidavide facontidavide merged commit aa1dcbe into main May 29, 2026
4 checks passed
@facontidavide facontidavide deleted the ci/release-multiplatform branch May 29, 2026 13:08
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