name: Cross-library benchmark # Audit finding R10: previously the cross-library benchmark ran on every push # and every pull-request to `main`, adding 5–10 minutes of build + bench time # per CI run with no consumer of the resulting artefact. The benchmark is now # scheduled (nightly at 03:00 UTC) and on-demand via `workflow_dispatch`. The # CI pipeline proper (.github/workflows/ci.yml) still verifies build / tests / # lints on every push and pull-request. on: schedule: # Nightly at 03:00 UTC. Pick a slot well away from common European / # American working-hours pushes to keep this off the critical path. - cron: "0 3 * * *" workflow_dispatch: inputs: size: description: "Number of bars per indicator (default 20000)" required: false default: "20000" iterations: description: "Batch iterations per indicator (default 10)" required: false default: "10" # Least-privilege default for the auto-injected GITHUB_TOKEN. The single job # only builds and uploads an artifact (upload-artifact uses the artifact # storage API, not the contents scope), so it never needs repo write (OpenSSF # Scorecard: Token-Permissions). permissions: contents: read env: CARGO_TERM_COLOR: always # Network-flake resilience: retry transient registry/DNS failures at the tool # level so a blip fetching crates.io / PyPI inside any build step (cargo, # maturin, pip) retries automatically instead of failing the job. Cargo treats # "couldn't resolve host" / connect / timeout as spurious and retries with # backoff; 10 attempts ride out a transient DNS blip on a runner. CARGO_NET_RETRY: "10" CARGO_NET_GIT_FETCH_WITH_CLI: "true" npm_config_fetch_retries: "5" npm_config_fetch_retry_maxtimeout: "120000" PIP_RETRIES: "5" PIP_DEFAULT_TIMEOUT: "120" jobs: cross-library-bench: name: Cross-library benchmark report runs-on: ubuntu-latest steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 continue-on-error: true # cache is an optimisation; never block on a stuck/slow restore timeout-minutes: 6 - name: Set up Python id: setup_python continue-on-error: true uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.11" cache: pip cache-dependency-path: .github/requirements/bench.txt - name: Wait before Python retry if: steps.setup_python.outcome == 'failure' shell: bash run: | echo "::warning::setup-python failed (likely CDN flake), waiting 30s before retry..." sleep 30 - name: Set up Python (retry) if: steps.setup_python.outcome == 'failure' uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.11" cache: pip cache-dependency-path: .github/requirements/bench.txt - name: Install Python deps + peer libs run: | python -m pip install --upgrade pip # Hash-locked deps (OpenSSF Scorecard PinnedDependencies). bench.yml # runs on a single Python version (3.11), so one lock file suffices. python -m pip install --require-hashes -r .github/requirements/bench.txt - name: Build Wickra wheel working-directory: bindings/python run: maturin build --release --out dist - name: Install Wickra wheel working-directory: bindings/python run: python -m pip install --find-links dist --force-reinstall wickra - name: Run cross-library benchmark working-directory: bindings/python # workflow_dispatch inputs are untrusted; pass them through the # environment and quote them rather than interpolating into the shell # command (OpenSSF Scorecard: Dangerous-Workflow). env: BENCH_SIZE: ${{ github.event.inputs.size || '20000' }} BENCH_ITERATIONS: ${{ github.event.inputs.iterations || '10' }} run: | python -m benchmarks.compare_libraries \ --size "$BENCH_SIZE" \ --iterations "$BENCH_ITERATIONS" \ --streaming-window 5000 --streaming-iterations 2 \ | tee benchmark.txt - name: Upload report uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: cross-library-bench path: bindings/python/benchmark.txt rust-cross-bench: name: Rust cross-library benchmark report runs-on: ubuntu-latest steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 continue-on-error: true # cache is an optimisation; never block on a stuck/slow restore timeout-minutes: 6 # Wickra vs the other Rust TA crates (kand, ta-rs, yata) on an identical # candle series — the like-for-like engine comparison with no binding # overhead. Streaming + batch, in crates/wickra-bench/benches/cross_lib.rs. - name: Run Rust cross-library benchmark run: cargo bench -p wickra-bench --bench cross_lib | tee rust_cross_bench.txt - name: Upload Rust report uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: rust-cross-bench path: rust_cross_bench.txt