ci: move the whole build matrix to GitHub Actions and remove CircleCI - #71
ci: move the whole build matrix to GitHub Actions and remove CircleCI#71RedFox20 wants to merge 11 commits into
Conversation
This starts the move of CI from CircleCI to GitHub Actions. The composite action takes the same inputs as the CircleCI ubuntu-build template, so a later change only adds the matrix. One job runs today, ubuntu-cpp20-asan-gcc13, and CircleCI keeps the whole matrix. The action writes the test count into the run summary, so a reader compares it against the CircleCI job of the same name without a scroll through the log. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN
A composite action has no job-level env block, so the test step ran without NO_NINJA and BUILD_WITH_MODULES. A modules job would then reconfigure without the flag and fall back to headers while it stayed green. One step now writes the environment into GITHUB_ENV, the way the CircleCI template does with its environment block. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN
The log now names the cmake, mama and compiler binaries the run used. It also shows which cmake wins when the runner image already carries one, and the Windows job needs the same line for the MSVC toolset. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN
The ubuntu-24.04 image carries CMake 3.31.6 in /usr/local/bin, which comes before /usr/bin on the path. make configure-cmake writes /usr/bin/cmake, so the runner never read the 3.30.9 it installed. The project floor is 3.25. The step named a version no build used, and it cost about 20 seconds a job. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN
A push to a branch with an open pull request matched both triggers, so the same job ran twice on one commit. Push now covers master alone, and a pull request covers every branch under review. The header comment also named a document this repository does not carry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7d33aa1667
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Two CircleCI checks failed on
Why these are not this pull requestThis branch adds two files under CircleCI compiled and ran master's tree. Both jobs also passed on What each failure is
The TSAN job passes every test case and then fails on one sanitizer warning. AGENTS.md What I did and did not doNo fix exists to port into this branch. I cannot re-run either job, because the CircleCI The timing assertion is a real weakness and it deserves its own change. AGENTS.md asks a The GitHub Actions job this pull request adds is green on the same commit: Generated by Claude Code |
The same three-part test decided COV_FLAGS in the build step and in the test step, and the second copy ran to 133 columns, over the 130 limit in recpp-review R8. The environment step now sets COVERAGE_ON, and each step reads it. The longest line drops to 107 columns. Only a gcc asan build makes coverage data, so one place decides it. All twelve compiler, sanitizer and coverage combinations produce the same flags as before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN
CircleCI on
|
| Check | On facd5c7 |
On 3012710 |
|---|---|---|
ubuntu-cpp20-tsan-clang18 |
failed | 13337 success |
ubuntu-cpp20-modules-clang21 |
failed | 13356 success |
One check fails, and it is a different one:
android-cpp20-r28b-clang-tidy-clang19 build 13348
test_event_loop.cpp:1059 test_event_loop::fork_join_with_timeout:
remaining => '0' must be greater than '0'
This is not this pull request either. The branch still changes no C++, and the emulated
Android runner ran master's tree.
The test forks a 10 ms task and a 100 ms task, joins with a 30 ms timeout, then asserts
that a fork is still running. That holds only while the runner keeps the ratio between
those delays. Issue #70 records a 10 ms sleep measured at 43 ms on these same runners, so a
30 ms join stretches to about 129 ms and both forks finish first.
That is the same family issue #70 already tracks, so I added it there as case 4 with the
mechanism and a fix: hold the slow fork on an event the test owns instead of a sleep.
No fix exists to port into this branch, and I still cannot re-run a CircleCI job, because
the API answers 403 without a token.
The GitHub Actions job this pull request adds is green on 3012710:
SUCCESS: All 31 test suites with 528/528 test cases passed!
Generated by Claude Code
All 28 jobs now run on GitHub Actions, one for one with the jobs CircleCI ran: 19 Linux compiler and sanitizer combinations, 4 Android NDK builds, 2 consumer integration builds, 2 Windows builds and the MIPS cross build. Three composite actions carry the shared steps. ubuntu-build already ran the Linux jobs. consumer-build and android-build are new. Windows moves to windows-2025-vs2026. Visual Studio 2026 runs on no CircleCI machine runner, which is what started this move, so the two MSVC jobs drop 2022 from their names. The Android jobs install a pinned NDK through sdkmanager, because the runner image carries other releases. The QEMU sysroot, the liblog source and the compile database helper were never CircleCI configuration. They move to .github/, and run_android_tests, run_clang_tidy and README.md follow them. run_clang_tidy also listed an NDK path that only the CircleCI image had, so it now reads ANDROID_HOME. The README badges point at the workflow, and the local CI section describes the mama commands a job runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN
…dings mama reads ANDROID_NDK_LATEST_HOME before ANDROID_NDK_HOME, and the runner image points it at its own NDK 29. Every Android job built with that one, so the r27 and r28b jobs did not test the release they name. The action now sets all three variables. The r27 job failed because run_android_tests then copied the r27 libc++ under a binary that NDK 29 built. The three gcc clang-tidy jobs never ran clang-tidy on CircleCI. That image carried no clang-tidy, and mama printed "clang-tidy not found! Static analysis will be disabled" and continued. The runner image carries clang-tidy 18, so the check runs now and reports two findings: - thread_pool.cpp parallel_for has a cognitive complexity of 32 against a threshold of 30. The spawn loop and the reuse loop share the task array, so one NOLINT line keeps them together instead of splitting the lifetime. - test_event_loop.cpp compared an int64 against a double literal. The literal is now an integer. Both were reproduced with clang-tidy 18 and both come back clean after the fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN
The gcc clang-tidy jobs run the check for the first time, and it stops the build at the first error, so each fix uncovered the next one. A full local sweep over all 60 translation units found the whole set at once. All of it sits in tests. Mechanical: - test_timer and test_sockets declared two variables in one statement. - test_modules left four parameters unnamed. - test_sockets built the assert condition inside the macro, so the check read a negated conjunction. A named bool holds it now. Two findings the check reads wrong for the context, so each takes a NOLINT and one line of why: - test_sprint writes 3.14159265358979 and the next line asserts that exact text. std::numbers::pi differs by 3.11e-15 and prints something else. - test_task passes the loop by reference into eight coroutines. The loop drives them, so it outlives every one. The file already suppressed the sibling check for capturing lambda coroutines. Verified: clang-tidy 18 over all 60 translation units reports no finding, and the five suites pass 99/99. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN
clang-analyzer reported a potential memory leak at the end of the move constructor of worker_cleanup_marker_task. The type holds a shared_ptr and allocates nothing, and the trace runs through the promise state inside async_task. The move constructor also leaves cleanup ownership behind on purpose, which the test needs and the analyzer reads as a lost allocation. A local sweep over all 60 translation units now reports no finding, and test_threadpool passes 28/28. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN
The entry named one job and two call sites. The same libc++-only suppression leaves a third site open, where a worker frees the runtime_error string that test_future::test_except_handler_chaining reads on another worker. All of them sit inside an uninstrumented libstdc++. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN
All 28 jobs pass on
|
Visual Studio 2026 runs on no CircleCI machine runner, and GitHub Actions carries
windows-2025-vs2026. This moves all 28 jobs, one for one, and deletes.circleci/config.yml.All 28 jobs pass on the current head.
What runs now
ubuntu-24.04ubuntu-24.04ubuntu-24.04windows-2025-vs2026ubuntu-24.04Three composite actions under
.github/actions/carry the shared steps:ubuntu-build,consumer-buildandandroid-build. Each writes its test count intothe run summary.
The two MSVC jobs drop
2022from their names. The whole matrix finishes in about nineminutes, against roughly the same wall time per job on three cores before.
What the move uncovered
Four bugs that CircleCI hid, each fixed here:
clang-tidyjobs never ran clang-tidy. That image carried noclang-tidy, so mama printed
clang-tidy not found! Static analysis will be disabledand the job went green. The runner image carries clang-tidy 18, so the check runs now.
It found 13 real items, all in
tests/plus one inthread_pool.cpp.ANDROID_NDK_LATEST_HOMEbeforeANDROID_NDK_HOME, and the runner image points it atits own NDK 29, so the r27 and r28b jobs did not test the release they name. The action
sets all three variables.
.gitignorehid the first composite action.build-*/matched.github/actions/build-and-test/. The directory is nowubuntu-build.make configure-cmakewas dead weight. The image ships CMake 3.31.6 in/usr/local/bin, ahead of the/usr/bin/cmakethat step writes, so no build ever readthe 3.30.9 it installed. The floor is 3.25.
The clang-tidy findings
Mechanical: split two declarations in
test_timerandtest_sockets, name fourparameters in
test_modules, and lift thetest_socketsassert condition into a namedbool so the check stops reading a negated conjunction inside the macro.
Four take a NOLINT and one line of why, because the check reads the context wrong:
thread_pool.cppparallel_for, complexity 32 against a threshold of 30. The spawnloop and the reuse loop share the task array, so they stay in one function.
test_sprintwrites3.14159265358979and the next line asserts that exact text.std::numbers::pidiffers by 3.11e-15 and prints something else.test_taskpasses the loop by reference into eight coroutines. The loop drives them,so it outlives every one.
test_threadpoolholds ashared_ptrand allocates nothing. The analyzer traces a leakthrough the promise state and reads a deliberate half-move as a lost allocation.
Every one was reproduced with clang-tidy 18 before the fix and comes back clean after. A
sweep over all 60 translation units reports nothing.
Files that were never CircleCI configuration
The QEMU sysroot, the
liblogsource and the compile database helper move from.circleci/to.github/.run_android_tests,run_clang_tidyandREADME.mdfollowthem.
run_clang_tidyalso printed an NDK path only the CircleCI image had, so it readsANDROID_HOMEnow.Two things a repository admin must do
No configuration was found in your projectas an error status on every commit. Thatis the one red mark left on this pull request, and no change in this branch can clear
it.
Known caveat
ubuntu-cpp23-tsan-gcc13fails intermittently. That isBUGS.mdB6: the C15 TSANsuppression is guarded
#if defined(__clang__)and its pattern is the libc++ spelling, sounder gcc nothing matches and the libstdc++ teardown races get reported. This branch adds
the third known call site to that entry and does not try to fix it.
🤖 Generated with Claude Code
https://claude.ai/code/session_01PJQak2qMQ4cXHtqEcpwimN