Skip to content

ci: move the whole build matrix to GitHub Actions and remove CircleCI - #71

Open
RedFox20 wants to merge 11 commits into
masterfrom
ci/migrate-to-gh-actions
Open

ci: move the whole build matrix to GitHub Actions and remove CircleCI#71
RedFox20 wants to merge 11 commits into
masterfrom
ci/migrate-to-gh-actions

Conversation

@RedFox20

@RedFox20 RedFox20 commented Sep 3, 2026

Copy link
Copy Markdown
Owner

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

Group Jobs Runner
Linux compiler and sanitizer matrix 19 ubuntu-24.04
Android NDK r27, r28b, r29, and r29 under Ninja 4 ubuntu-24.04
Consumer integration, gcc-14 and clang-21 2 ubuntu-24.04
Windows build and consumer 2 windows-2025-vs2026
MIPS cross build 1 ubuntu-24.04

Three composite actions under .github/actions/ carry the shared steps:
ubuntu-build, consumer-build and android-build. Each writes its test count into
the run summary.

The two MSVC jobs drop 2022 from their names. The whole matrix finishes in about nine
minutes, against roughly the same wall time per job on three cores before.

What the move uncovered

Four bugs that CircleCI hid, each fixed here:

  1. The three gcc clang-tidy jobs never ran clang-tidy. That image carried no
    clang-tidy, so mama printed clang-tidy not found! Static analysis will be disabled
    and 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 in thread_pool.cpp.
  2. All four Android jobs built with the wrong NDK. mama reads
    ANDROID_NDK_LATEST_HOME before ANDROID_NDK_HOME, and the runner image points it at
    its own NDK 29, so the r27 and r28b jobs did not test the release they name. The action
    sets all three variables.
  3. .gitignore hid the first composite action. build-*/ matched
    .github/actions/build-and-test/. The directory is now ubuntu-build.
  4. make configure-cmake was dead weight. The image ships CMake 3.31.6 in
    /usr/local/bin, ahead of the /usr/bin/cmake that step writes, so no build ever read
    the 3.30.9 it installed. The floor is 3.25.

The clang-tidy findings

Mechanical: split two declarations in test_timer and test_sockets, name four
parameters in test_modules, and lift the test_sockets assert condition into a named
bool 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.cpp parallel_for, complexity 32 against a threshold of 30. The spawn
    loop and the reuse loop share the task array, so they stay in one function.
  • 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.
  • test_threadpool holds a shared_ptr and allocates nothing. The analyzer traces a leak
    through 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 liblog source and the compile database helper move from
.circleci/ to .github/. run_android_tests, run_clang_tidy and README.md follow
them. run_clang_tidy also printed an NDK path only the CircleCI image had, so it reads
ANDROID_HOME now.

Two things a repository admin must do

  1. Turn off the CircleCI GitHub integration. With the config gone it posts
    No configuration was found in your project as an error status on every commit. That
    is the one red mark left on this pull request, and no change in this branch can clear
    it.
  2. Repoint branch protection. All 28 check names changed.

Known caveat

ubuntu-cpp23-tsan-gcc13 fails intermittently. That is BUGS.md B6: the C15 TSAN
suppression is guarded #if defined(__clang__) and its pattern is the libc++ spelling, so
under 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

RedFox20 and others added 5 commits September 3, 2026 11:18
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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

Comment thread .github/actions/ubuntu-build/action.yml Outdated
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T12:30:11.381479Z 9e6aba9 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

RedFox20 commented Sep 3, 2026

Copy link
Copy Markdown
Owner Author

Two CircleCI checks failed on facd5c7. Neither one belongs to this pull request.

Check Build What it reports
ubuntu-cpp20-tsan-clang18 13318 all 528 test cases pass, then ThreadSanitizer: reported 1 warnings exits 66
ubuntu-cpp20-modules-clang21 13324 test_timer.cpp:656 cpu_delta => '44483' must be greater or equal than '45000'

Why these are not this pull request

This branch adds two files under .github/. It changes no C++ source, no
CMakeLists.txt, no mamafile.py and no .circleci/config.yml:

$ git diff --name-only origin/master..HEAD
.github/actions/ubuntu-build/action.yml
.github/workflows/ci.yml

$ git diff --name-only origin/master..HEAD -- src/ tests/ CMakeLists.txt mamafile.py Makefile cmake/
(empty)

CircleCI compiled and ran master's tree. Both jobs also passed on 61e8c6b, an earlier
commit of this same branch that carried the same C++ tree.

What each failure is

test_timer::proc_cpu_times spins for 50000us and allows a 5000us margin, so the bound is
45000us. The runner measured 44483us, which is 1 percent under. That is a timing tolerance
on a shared 3-core runner, not a defect this branch introduced.

The TSAN job passes every test case and then fails on one sanitizer warning. AGENTS.md
treats TSAN as a suggestion, not a gate.

What I did and did not do

No fix exists to port into this branch. I cannot re-run either job, because the CircleCI
API answers 403 without a token.

The timing assertion is a real weakness and it deserves its own change. AGENTS.md asks a
test to wait on an event, not on the clock. That fix belongs in a separate pull request,
because this one adds no C++, and a C++ change needs the full suite, clang-tidy, and the
Windows and Android gates.

The GitHub Actions job this pull request adds is green on the same commit:

SUCCESS: All 31 test suites with 528/528 test cases passed!

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

RedFox20 commented Sep 3, 2026

Copy link
Copy Markdown
Owner Author

CircleCI on 3012710: 27 of 28 pass

The two checks from my earlier comment both pass on this head, which settles them:

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

RedFox20 and others added 5 commits September 3, 2026 11:43
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
@RedFox20 RedFox20 changed the title ci: start the move from CircleCI to GitHub Actions with one Linux job ci: move the whole build matrix to GitHub Actions and remove CircleCI Sep 3, 2026

RedFox20 commented Sep 3, 2026

Copy link
Copy Markdown
Owner Author

All 28 jobs pass on 6020df6

That includes ubuntu-cpp23-tsan-gcc13, which failed on the two commits before this one.
It is the intermittent BUGS.md B6 race, not a persistent failure, and this branch adds
its third known call site to that entry.

One red mark remains, and no commit here can clear it

CircleCI Pipeline — error
No configuration was found in your project.

That is the CircleCI GitHub integration reacting to the deleted .circleci/config.yml. It
posts on every commit. Turning the integration off is a repository admin action.

The other admin action is branch protection. All 28 check names changed, and the two MSVC
jobs dropped 2022, so the old required checks read as missing until someone repoints
them.


Generated by Claude Code

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