name: CI # THE BUILD TOOL IS NAMED HERE AND NOWHERE ELSE. # # This workflow used to install it through `.xlings.json', a workspace pin that # `actions/checkout' places in the working directory before anything else runs. # Two consequences followed and neither was visible in a passing log: # # * every `mcpp' invocation in this repository resolved to the pinned version # rather than to the one the job had installed, because that pin is read # from the current directory; # * the pin named 0.0.13, from 2026-05. The index no longer serves the # toolchain packages a build tool of that age asks for, so `mcpp build' # ended at `package 'xim:glibc@>=2.39' not found' before compiling a line. # # The pin file is removed. The version is an environment variable, so the two # places that need it, the install and the cache, cannot drift apart again. on: push: branches: [master] pull_request: # A CANARY, BECAUSE THIS ROTTED WHERE NOTHING WOULD LOOK. # # The breakage above occurred between 2026-07-11 and 2026-08-29 and nothing # reported it: master received no pushes in that window, so the first run to # meet it was a contributor's pull request, whose author had no way to # distinguish a broken environment from a broken change. schedule: - cron: '0 6 * * 1' workflow_dispatch: env: # 2026.9.30.2 and not earlier: 2026.9.28.3 refused x86_64-windows-musl # ("cannot be built on this host") before the dependency graph that supplies # its system side, openkal-llvm-runtime, had been resolved. MCPP_VERSION: 2026.9.30.2 XLINGS_VERSION: v2026.8.17.2 XLINGS_NON_INTERACTIVE: '1' jobs: # ── Linux, under both standard libraries ───────────────────────────────────── # # WHY TWO TOOLCHAINS. The job used to run whatever mcpp installed by default on # a fresh runner, which is gcc and libstdc++, and nothing ran the tests under # libc++. libc++ is the standard library of every other job below — macOS and # openkal — and of mcpp's own default where a developer has chosen llvm; the # cancellation tests met a libc++ defect that libstdc++ does not have # (tests/test_cancel.cpp, at the top), and no job here could have seen it. build: name: build + test (linux x86_64, ${{ matrix.toolchain }}) runs-on: ubuntu-latest timeout-minutes: 45 strategy: fail-fast: false matrix: toolchain: ['gcc@16.1.0', 'llvm@22.1.8'] steps: - uses: actions/checkout@v4 - name: Install xlings run: | # --retry-all-errors and not --retry alone: the first covers a # transient HTTP status and a timeout, and what this step actually # meets is a failure of the transport. Observed in this ecosystem as # `curl: (35) Recv failure: Connection reset by peer', thirteen # seconds into a job, before anything was built. curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 \ https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh \ | bash -s "$XLINGS_VERSION" echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" - name: Install mcpp run: | xlings update xlings install "mcpp@$MCPP_VERSION" -y -g # The version this job runs is the version it prints. A pin that is # not read is the failure this file exists to stop repeating. mcpp --version # A runner outside China reaches the mirrors this names. The CN set is # for a developer's machine and is slower or unreachable from here. mcpp self config --mirror GLOBAL # The toolchain mcpp bootstraps is decided by the version pinned above, so # that version and the toolchain are the whole of the key. - name: Cache mcpp sandbox uses: actions/cache@v4 with: path: ~/.xlings/data/xpkgs/xim-x-mcpp/${{ env.MCPP_VERSION }}/registry key: mcpp-sandbox-${{ runner.os }}-mcpp${{ env.MCPP_VERSION }}-${{ matrix.toolchain }} - name: Select the toolchain run: | spec='${{ matrix.toolchain }}' mcpp toolchain install "${spec%@*}" "${spec#*@}" mcpp toolchain default "$spec" mcpp toolchain list - name: Build with mcpp run: mcpp build # Most of these are hermetic: `test_framing` needs nothing, and `test_pool`, # `test_proxy` and `test_cancel` script their own servers on 127.0.0.1. The # eight in `test_download` and `test_resolver` reach httpbin.org and # one.one.one.one; a network failure among them is distinguishable in the # log by the endpoint it names. - name: Run tests run: mcpp test # A TEMPLATE IS PART OF THE RELEASE THAT SHIPS IT, AND `mcpp new` CAN ONLY # REACH ONE THAT IS ALREADY PUBLISHED. Checking them after the release # makes the first person to run `mcpp new` the one who finds out they do # not compile, so this renders them the way the scaffolder does and builds # them against this commit. It has already caught one: `import std` # carries no `stdout` macro, so a progress bar flushed through it did not # compile in a generated project while compiling fine in this repository. - name: Smoke-test the project templates run: bash tools/template_smoke.sh # ── macOS ──────────────────────────────────────────────────────────────────── # # THE README NAMES macOS, AND UNTIL THIS JOB NOTHING RAN THERE. The # differences are real and in this library's own code: SO_NOSIGPIPE rather # than MSG_NOSIGNAL (src/platform.cppm), /dev/urandom as mbedTLS's entropy, # the system CA bundle's location, and poll(2) on a socket whose connect is in # progress, which the cancellation slices wait on. macos: name: build + test (macos arm64, llvm@22.1.8) runs-on: macos-15 timeout-minutes: 45 steps: - uses: actions/checkout@v4 - name: Install xlings run: | curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 \ https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh \ | bash -s "$XLINGS_VERSION" echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" - name: Install mcpp run: | xlings update xlings install "mcpp@$MCPP_VERSION" -y -g mcpp --version mcpp self config --mirror GLOBAL - name: Cache mcpp sandbox uses: actions/cache@v4 with: path: ~/.xlings/data/xpkgs/xim-x-mcpp/${{ env.MCPP_VERSION }}/registry key: mcpp-sandbox-${{ runner.os }}-mcpp${{ env.MCPP_VERSION }}-llvm@22.1.8 - name: Select the toolchain run: | mcpp toolchain install llvm 22.1.8 mcpp toolchain default llvm@22.1.8 mcpp toolchain list - name: Build with mcpp run: mcpp build - name: Run tests run: mcpp test # ── The same sources, on a different kernel ABI ────────────────────────────── # # WHY THIS IS A SEPARATE JOB AND NOT A STEP. It resolves a different toolchain # (llvm rather than gcc) and a different everything below the program — musl # ported onto openkal, libc++ configured for that musl, and openkal-linux # beneath both. Sharing a job would mean sharing a cache key between two # stacks that have nothing in common but the source tree. # # WHAT IT CATCHES THAT THE JOB ABOVE CANNOT. `Socket::write` sends with # MSG_NOSIGNAL and `connect_addrinfo` sets SO_NOSIGPIPE, and which of those # exists is decided by the C library rather than by the operating system — # openkal-musl defines the first and not the second. A `#ifdef` that is wrong # about that compiles cleanly here and fails there, which is exactly how # 5e7d66f reached master. # # FOUR TARGETS, THE ONES mcpp-index MEASURES tinyhttps ON (its # tests/openkal/pins.toml). aarch64-linux-musl runs under qemu, through the # runner examples/openkal names. x86_64-windows-musl is built here and run on # Windows itself by the job after this one: wine took anywhere from four to # more than thirty minutes to install on these runners, and Windows is the # system the program is for. The Windows target is where every handshake used # to fail for want of entropy (src/tls.cppm); it has no name resolution, so its # network step reports "no network" and the cancellation check, which needs # none, is what runs. openkal: name: build + run (${{ matrix.target }}, above openkal) runs-on: ubuntu-24.04 timeout-minutes: 45 strategy: fail-fast: false matrix: target: ['x86_64-linux-gnu', 'x86_64-linux-musl', 'x86_64-windows-musl', 'aarch64-linux-musl'] steps: - uses: actions/checkout@v4 - name: Install xlings run: | curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 \ https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh \ | bash -s "$XLINGS_VERSION" echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" - name: Install mcpp run: | xlings update xlings install "mcpp@$MCPP_VERSION" -y -g mcpp --version mcpp self config --mirror GLOBAL - name: Install the runner if: matrix.target == 'aarch64-linux-musl' timeout-minutes: 15 env: DEBIAN_FRONTEND: noninteractive run: | sudo apt-get update -qq sudo apt-get install -y -qq --no-install-recommends qemu-user > /dev/null qemu-aarch64 --version | head -1 - name: Cache mcpp sandbox uses: actions/cache@v4 with: path: ~/.xlings/data/xpkgs/xim-x-mcpp/${{ env.MCPP_VERSION }}/registry key: mcpp-openkal-${{ runner.os }}-mcpp${{ env.MCPP_VERSION }}-${{ matrix.target }} # The example builds the library from this checkout (`path = "../.."`) and # takes the stack beneath it from the index, so what is tested is this # commit against the published openkal packages. # # The toolchain is named, as mcpp-index's measurement names it, and # installed beforehand: on a fresh runner x86_64-windows-musl was refused # as "cannot be built on this host" while llvm was not yet installed, though # the Linux targets, which install it on demand, built. # # It cancels a handshake against a local listener, which needs no network, # and then makes one HTTPS request; it reports "no network" rather than # failing when there is none, so a runner without egress reports "not run" # instead of "broken". A certificate the client refused is a failure, not # an absence of network. The build, the link, the framing parsers and the # cancellation are checked either way. - name: Install llvm run: mcpp toolchain install llvm 22.1.8 - name: Build and run above openkal if: matrix.target != 'x86_64-windows-musl' working-directory: examples/openkal run: | set -o pipefail mcpp run --toolchain llvm@22.1.8 --target '${{ matrix.target }}' 2>&1 | tee run.log grep -q '^cancellation: ok' run.log # A static PE executable that imports only system DLLs, so it runs on any # Windows as it is. - name: Build for Windows above openkal if: matrix.target == 'x86_64-windows-musl' working-directory: examples/openkal run: | mcpp build --toolchain llvm@22.1.8 --target x86_64-windows-musl mkdir -p "$RUNNER_TEMP/smoke" cp target/x86_64-windows-musl/*/bin/smoke.exe "$RUNNER_TEMP/smoke/" - uses: actions/upload-artifact@v4 if: matrix.target == 'x86_64-windows-musl' with: name: smoke-x86_64-windows-musl path: ${{ runner.temp }}/smoke/smoke.exe if-no-files-found: error openkal-windows: name: run (x86_64-windows-musl, above openkal, on Windows) needs: openkal runs-on: windows-2022 timeout-minutes: 15 defaults: run: shell: bash steps: - uses: actions/download-artifact@v4 with: name: smoke-x86_64-windows-musl # Above openkal the Windows certificate store is not read (README, # "Platforms"), so the program is given a bundle: Git for Windows's, copied # beside it, because openkal resolves a relative name against the working # directory and names no absolute path. - name: Run above openkal run: | set -o pipefail cp "/c/Program Files/Git/mingw64/etc/ssl/certs/ca-bundle.crt" ca-bundle.crt SSL_CERT_FILE=ca-bundle.crt ./smoke.exe 2>&1 | tee run.log grep -q '^cancellation: ok' run.log # ── Windows ────────────────────────────────────────────────────────────────── # # WHY THIS JOB EXISTS. Windows keeps no bundle file, so on a Windows Sockets # build `load_ca_certs` reads the system's ROOT store through the Win32 API # (src/ca_bundle.cppm). That code is compiled on no other platform, and a job # elsewhere can show neither that it builds nor that the roots it returns # verify a real certificate chain. `test_ca_store` asserts both. # # AND THE HERMETIC TESTS RUN HERE TOO. They script their own servers on # 127.0.0.1, and Windows Sockets is where `poll` is `WSAPoll`, a connect in # progress is reported differently, and the cancellation slices were never # run before this job ran them. windows: name: build + test (windows x86_64, ${{ matrix.toolchain }}) runs-on: windows-2022 timeout-minutes: 45 strategy: fail-fast: false matrix: toolchain: ['msvc@system', 'llvm@20.1.7'] defaults: run: shell: bash steps: - uses: actions/checkout@v4 - name: Install xlings shell: pwsh run: | irm https://d2learn.org/xlings-install.ps1.txt | iex "$env:USERPROFILE\.xlings\subos\current\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Install mcpp run: | xlings update xlings install "mcpp@$MCPP_VERSION" -y -g mcpp --version mcpp self config --mirror GLOBAL - name: Select the toolchain run: | spec='${{ matrix.toolchain }}' case "$spec" in msvc*) mcpp toolchain default msvc ;; *) mcpp toolchain install "${spec%@*}" "${spec#*@}" mcpp toolchain default "$spec" ;; esac mcpp toolchain list - name: The system store verifies a public chain run: | mcpp test test_ca_store 2>&1 | tee tests.log grep -q '^test_ca_store \.\.\. ok' tests.log - name: The hermetic tests run: | set -o pipefail for t in test_framing test_tls_verify test_pool test_proxy test_cancel; do mcpp test "$t" 2>&1 | tee "$t.log" grep -q "^$t \.\.\. ok" "$t.log" done