From b5249c5c1cc4c4b5bbf0c41370b508f9f41843ff Mon Sep 17 00:00:00 2001 From: Guillaume Lagrange Date: Fri, 10 Apr 2026 18:01:58 +0200 Subject: [PATCH 1/5] feat(go-runner): allow overriding instrument-hooks path via env var Add support for `CODSPEED_INSTRUMENT_HOOKS_DIR` environment variable to skip downloading and use a local instrument-hooks directory instead. This enables development workflows and running on platforms where the pre-built hooks are not available (e.g. macOS). Refs COD-2459 Co-Authored-By: Claude --- go-runner/src/runner/overlay/instrument_hooks.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/go-runner/src/runner/overlay/instrument_hooks.rs b/go-runner/src/runner/overlay/instrument_hooks.rs index ecc1c33..0aee736 100644 --- a/go-runner/src/runner/overlay/instrument_hooks.rs +++ b/go-runner/src/runner/overlay/instrument_hooks.rs @@ -11,6 +11,14 @@ const INSTRUMENT_HOOKS_COMMIT: &str = "0c971823b17cb5a3bbd0cce4411cbee2c6fe4317" /// Get the instrument-hooks directory, downloading if necessary /// Downloads to /tmp/codspeed-instrument-hooks-{commit}/ pub fn download_instrument_hooks(temp_dir: &TempDir) -> Result { + // Allow overriding with a local path for development + if let Ok(local_path) = std::env::var("CODSPEED_INSTRUMENT_HOOKS_DIR") { + let path = PathBuf::from(local_path); + ensure!(path.exists(), "CODSPEED_INSTRUMENT_HOOKS_DIR path does not exist: {:?}", path); + debug!("Using local instrument-hooks at {:?}", path); + return Ok(path); + } + let hooks_dir = temp_dir .path() .join(format!("instrument-hooks-{}", INSTRUMENT_HOOKS_COMMIT)); From 112b3b05df6de70417d804216681f57b901bdffc Mon Sep 17 00:00:00 2001 From: Guillaume Lagrange Date: Fri, 10 Apr 2026 18:09:33 +0200 Subject: [PATCH 2/5] feat: simplify C FFI integration and make it compatible with macos --- go-runner/overlay/codspeed.go | 4 ++-- go-runner/overlay/instrument-hooks.go | 22 ++++++++----------- .../src/runner/overlay/instrument_hooks.rs | 8 +++++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/go-runner/overlay/codspeed.go b/go-runner/overlay/codspeed.go index cd04335..a5f0458 100644 --- a/go-runner/overlay/codspeed.go +++ b/go-runner/overlay/codspeed.go @@ -15,7 +15,7 @@ import ( func writeGoEnvironment(hooks *InstrumentHooks) { hooks.SetEnvironment("go", "version", runtime.Version()) - hooks.WriteEnvironment(uint32(os.Getpid())) + hooks.WriteEnvironment(int32(os.Getpid())) } type codspeed struct { @@ -244,7 +244,7 @@ func saveCodspeedResults(b *B, r BenchmarkResult, benchName string) { defer file.Close() // Send pid and executed benchmark to the runner - b.codspeed.instrument_hooks.SetExecutedBenchmark(uint32(os.Getpid()), benchUri) + b.codspeed.instrument_hooks.SetExecutedBenchmark(int32(os.Getpid()), benchUri) } func (b *B) sendAccumulatedTimestamps() { diff --git a/go-runner/overlay/instrument-hooks.go b/go-runner/overlay/instrument-hooks.go index 1a254ca..b1bc520 100644 --- a/go-runner/overlay/instrument-hooks.go +++ b/go-runner/overlay/instrument-hooks.go @@ -3,10 +3,6 @@ package testing /* #cgo CFLAGS: -I@@INSTRUMENT_HOOKS_DIR@@/includes -Wno-format -Wno-format-security #include "@@INSTRUMENT_HOOKS_DIR@@/dist/core.c" - -#define MARKER_TYPE_BENCHMARK_START c_MARKER_TYPE_BENCHMARK_START__247 -#define MARKER_TYPE_BENCHMARK_END c_MARKER_TYPE_BENCHMARK_END__248 -typedef struct instrument_hooks_InstrumentHooks__547 InstrumentHooks; */ import "C" import ( @@ -51,7 +47,7 @@ func (i *InstrumentHooks) SetIntegration(name, version string) { defer C.free(unsafe.Pointer(nameC)) defer C.free(unsafe.Pointer(versionC)) - C.instrument_hooks_set_integration(i.hooks, (*C.uint8_t)(unsafe.Pointer(nameC)), (*C.uint8_t)(unsafe.Pointer(versionC))) + C.instrument_hooks_set_integration(i.hooks, nameC, versionC) } func (i *InstrumentHooks) StartBenchmark() { @@ -66,14 +62,14 @@ func (i *InstrumentHooks) StopBenchmark() { } } -func (i *InstrumentHooks) SetExecutedBenchmark(pid uint32, name string) { +func (i *InstrumentHooks) SetExecutedBenchmark(pid int32, name string) { if i.hooks == nil { return } nameC := C.CString(name) defer C.free(unsafe.Pointer(nameC)) - C.instrument_hooks_set_executed_benchmark(i.hooks, C.uint(pid), (*C.uint8_t)(unsafe.Pointer(nameC))) + C.instrument_hooks_set_executed_benchmark(i.hooks, C.int32_t(pid), nameC) } func (i *InstrumentHooks) IsInstrumented() bool { @@ -91,9 +87,9 @@ func (i *InstrumentHooks) AddBenchmarkTimestamps(startTimestamp, endTimestamp ui if i.hooks == nil { return } - pid := uint32(os.Getpid()) - C.instrument_hooks_add_marker(i.hooks, C.uint32_t(pid), C.MARKER_TYPE_BENCHMARK_START, C.uint64_t(startTimestamp)) - C.instrument_hooks_add_marker(i.hooks, C.uint32_t(pid), C.MARKER_TYPE_BENCHMARK_END, C.uint64_t(endTimestamp)) + pid := C.int32_t(os.Getpid()) + C.instrument_hooks_add_marker(i.hooks, C.int32_t(pid), C.MARKER_TYPE_BENCHMARK_START, C.uint64_t(startTimestamp)) + C.instrument_hooks_add_marker(i.hooks, C.int32_t(pid), C.MARKER_TYPE_BENCHMARK_END, C.uint64_t(endTimestamp)) } func (i *InstrumentHooks) SetEnvironment(sectionName, key, value string) { @@ -107,12 +103,12 @@ func (i *InstrumentHooks) SetEnvironment(sectionName, key, value string) { defer C.free(unsafe.Pointer(keyC)) defer C.free(unsafe.Pointer(valueC)) - C.instrument_hooks_set_environment(i.hooks, (*C.uint8_t)(unsafe.Pointer(sectionNameC)), (*C.uint8_t)(unsafe.Pointer(keyC)), (*C.uint8_t)(unsafe.Pointer(valueC))) + C.instrument_hooks_set_environment(i.hooks, sectionNameC, keyC, valueC) } -func (i *InstrumentHooks) WriteEnvironment(pid uint32) { +func (i *InstrumentHooks) WriteEnvironment(pid int32) { if i.hooks == nil { return } - C.instrument_hooks_write_environment(i.hooks, C.uint32_t(pid)) + C.instrument_hooks_write_environment(i.hooks, C.int32_t(pid)) } diff --git a/go-runner/src/runner/overlay/instrument_hooks.rs b/go-runner/src/runner/overlay/instrument_hooks.rs index 0aee736..acf6a55 100644 --- a/go-runner/src/runner/overlay/instrument_hooks.rs +++ b/go-runner/src/runner/overlay/instrument_hooks.rs @@ -6,7 +6,7 @@ use tar::Archive; use tempfile::TempDir; const INSTRUMENT_HOOKS_REPO: &str = "CodSpeedHQ/instrument-hooks"; -const INSTRUMENT_HOOKS_COMMIT: &str = "0c971823b17cb5a3bbd0cce4411cbee2c6fe4317"; +const INSTRUMENT_HOOKS_COMMIT: &str = "ecdf31a3afd0fb879823e40df65129ec823d374b"; /// Get the instrument-hooks directory, downloading if necessary /// Downloads to /tmp/codspeed-instrument-hooks-{commit}/ @@ -14,7 +14,11 @@ pub fn download_instrument_hooks(temp_dir: &TempDir) -> Result { // Allow overriding with a local path for development if let Ok(local_path) = std::env::var("CODSPEED_INSTRUMENT_HOOKS_DIR") { let path = PathBuf::from(local_path); - ensure!(path.exists(), "CODSPEED_INSTRUMENT_HOOKS_DIR path does not exist: {:?}", path); + ensure!( + path.exists(), + "CODSPEED_INSTRUMENT_HOOKS_DIR path does not exist: {:?}", + path + ); debug!("Using local instrument-hooks at {:?}", path); return Ok(path); } From 42986231a2a70a97e83443433c69ddfbfd7419c6 Mon Sep 17 00:00:00 2001 From: Guillaume Lagrange Date: Fri, 10 Apr 2026 18:10:18 +0200 Subject: [PATCH 3/5] ci: add basic macos CI test --- .github/workflows/ci.yml | 56 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bff068..e090b55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go-version: ['1.24.x', '1.25.x'] + go-version: ["1.24.x", "1.25.x"] env: # Needed for the quic-go test GOEXPERIMENT: ${{ matrix.go-version == '1.24.x' && 'synctest' || '' }} @@ -47,7 +47,14 @@ jobs: runs-on: codspeed-macro strategy: matrix: - target: [example, example/compat, example/timing, example/very/nested/module, example/external] + target: + [ + example, + example/compat, + example/timing, + example/very/nested/module, + example/external, + ] steps: - uses: actions/checkout@v4 with: @@ -66,6 +73,48 @@ jobs: working-directory: example run: cargo r --release --manifest-path ../go-runner/Cargo.toml -- test -bench=. ${{ matrix.target }} + example-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: actions/setup-go@v6 + with: + go-version: '1.25.x' + - uses: moonrepo/setup-rust@v1 + with: + cache-target: release + + - name: Run the benchmarks + run: cargo r --release --manifest-path ../go-runner/Cargo.toml -- test -bench=. example -benchtime=500ms + working-directory: example + env: + CODSPEED_GO_PKG_VERSION: ${{ github.head_ref || github.ref_name }} + + walltime-macos-test: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: actions/setup-go@v6 + with: + go-version: "1.25.x" + - uses: ./.github/actions/rust-install + - run: cargo build --release + + - name: Run the benchmarks + uses: CodSpeedHQ/action@main + env: + CODSPEED_SKIP_UPLOAD: "true" + with: + run: cargo r --release --manifest-path ../go-runner/Cargo.toml -- test -bench=. example -benchtime=500ms + working-directory: example + mode: walltime + # TODO: Remove this once the runner has been released with macos support + runner-version: branch:main + go-runner-benchmarks: runs-on: codspeed-macro steps: @@ -82,7 +131,7 @@ jobs: - uses: actions/setup-go@v5 with: - go-version: '1.25' + go-version: "1.25" - name: Build the benchmark target(s) run: cargo codspeed build -m walltime @@ -100,6 +149,7 @@ jobs: - lint - tests - compat-integration-test-walltime + - walltime-macos-test - go-runner-benchmarks steps: - uses: re-actors/alls-green@release/v1 From c43b721378a334ba803fdeff82888c37d5f21a27 Mon Sep 17 00:00:00 2001 From: Guillaume Lagrange Date: Fri, 10 Apr 2026 18:19:20 +0200 Subject: [PATCH 4/5] ci: get rid of the moonrepo/setup-rust action --- .github/actions/rust-install/action.yml | 10 +++++++ .github/workflows/ci.yml | 36 +++++-------------------- 2 files changed, 16 insertions(+), 30 deletions(-) create mode 100644 .github/actions/rust-install/action.yml diff --git a/.github/actions/rust-install/action.yml b/.github/actions/rust-install/action.yml new file mode 100644 index 0000000..fa03f72 --- /dev/null +++ b/.github/actions/rust-install/action.yml @@ -0,0 +1,10 @@ +name: "Install Rust toolchain" +description: "Install the Rust toolchain from rust-toolchain.toml and cache cargo artifacts" + +runs: + using: "composite" + steps: + - name: Install rust-toolchain.toml + shell: bash + run: rustup show + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e090b55..fefb0cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: with: submodules: true - uses: actions/setup-go@v5 - - uses: moonrepo/setup-rust@v1 + - uses: ./.github/actions/rust-install - uses: pre-commit/action@v3.0.1 with: extra_args: --all-files @@ -36,7 +36,7 @@ jobs: go-version: ${{ matrix.go-version }} - uses: taiki-e/install-action@cargo-nextest - - uses: moonrepo/setup-rust@v1 + - uses: ./.github/actions/rust-install - run: | cd go-runner cargo nextest run --all @@ -60,9 +60,7 @@ jobs: with: submodules: true - uses: actions/setup-go@v5 - - uses: moonrepo/setup-rust@v1 - with: - cache-target: release + - uses: ./.github/actions/rust-install - name: Run the benchmarks uses: CodSpeedHQ/action@main @@ -73,25 +71,6 @@ jobs: working-directory: example run: cargo r --release --manifest-path ../go-runner/Cargo.toml -- test -bench=. ${{ matrix.target }} - example-macos: - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - with: - submodules: true - - uses: actions/setup-go@v6 - with: - go-version: '1.25.x' - - uses: moonrepo/setup-rust@v1 - with: - cache-target: release - - - name: Run the benchmarks - run: cargo r --release --manifest-path ../go-runner/Cargo.toml -- test -bench=. example -benchtime=500ms - working-directory: example - env: - CODSPEED_GO_PKG_VERSION: ${{ github.head_ref || github.ref_name }} - walltime-macos-test: runs-on: macos-latest steps: @@ -122,12 +101,9 @@ jobs: with: lfs: true submodules: true - - name: Setup rust toolchain, cache and cargo-codspeed binary - uses: moonrepo/setup-rust@v1 - with: - channel: stable - cache-target: release - bins: cargo-codspeed + - uses: ./.github/actions/rust-install + - name: Install cargo-codspeed + run: cargo install cargo-codspeed - uses: actions/setup-go@v5 with: From 40963b6b343ee4802e7a50af256eeb76a5994f8a Mon Sep 17 00:00:00 2001 From: Guillaume Lagrange Date: Fri, 10 Apr 2026 18:35:55 +0200 Subject: [PATCH 5/5] feat: add apple darwin to the release pipeline --- .github/workflows/release.yml | 38 +++++++++++++++++------------------ dist-workspace.toml | 4 ++-- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index beed208..d9aa406 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,7 +56,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: persist-credentials: false submodules: recursive @@ -64,9 +64,9 @@ jobs: # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash - run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.29.0/cargo-dist-installer.sh | sh" + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.31.0/cargo-dist-installer.sh | sh" - name: Cache dist - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cargo-dist-cache path: ~/.cargo/bin/dist @@ -82,7 +82,7 @@ jobs: cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: artifacts-plan-dist-manifest path: plan-dist-manifest.json @@ -116,7 +116,7 @@ jobs: - name: enable windows longpaths run: | git config --global core.longpaths true - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: persist-credentials: false submodules: recursive @@ -131,7 +131,7 @@ jobs: run: ${{ matrix.install_dist.run }} # Get the dist-manifest - name: Fetch local artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: pattern: artifacts-* path: target/distrib/ @@ -158,7 +158,7 @@ jobs: cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: artifacts-build-local-${{ join(matrix.targets, '_') }} path: | @@ -175,19 +175,19 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: persist-credentials: false submodules: recursive - name: Install cached dist - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: pattern: artifacts-* path: target/distrib/ @@ -205,7 +205,7 @@ jobs: cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: artifacts-build-global path: | @@ -217,27 +217,27 @@ jobs: - plan - build-local-artifacts - build-global-artifacts - # Only run if we're "publishing", and only if local and global didn't fail (skipped is fine) - if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} + # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) + if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "ubuntu-22.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: persist-credentials: false submodules: recursive - name: Install cached dist - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: pattern: artifacts-* path: target/distrib/ @@ -250,14 +250,14 @@ jobs: cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: # Overwrite the previous copy name: artifacts-dist-manifest path: dist-manifest.json # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: pattern: artifacts-* path: artifacts @@ -290,7 +290,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: persist-credentials: false submodules: recursive diff --git a/dist-workspace.toml b/dist-workspace.toml index a1b1b77..511008a 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -6,13 +6,13 @@ members = ["cargo:."] # Whether to consider the binaries in a package for distribution (defaults true) dist = true # The preferred dist version to use in CI (Cargo.toml SemVer syntax) -cargo-dist-version = "0.29.0" +cargo-dist-version = "0.31.0" # CI backends to support ci = "github" # The installers to generate for each app installers = ["shell"] # Target platforms to build apps for (Rust target-triple syntax) -targets = ["aarch64-unknown-linux-musl", "x86_64-unknown-linux-musl"] +targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-musl", "x86_64-unknown-linux-musl"] # Path that installers should place binaries in install-path = "CARGO_HOME" # Whether to install an updater program