name: CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch: {}
schedule:
- cron: "23 4 * * 1"
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: rustsec/audit-check@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
deny-warnings: false
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- run: cargo clippy --all-targets -- -D warnings
coverage:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: install cargo-llvm-cov
uses: taiki-e/install-action@v2
with:
tool: cargo-llvm-cov
- name: instrumented tests
run: |
rustup component add llvm-tools-preview
cargo llvm-cov --lcov --ignore-filename-regex 'src/mcp' --output-path lcov.info
- name: update gh-pages badge
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
env:
GH_TOKEN: ${{ github.token }}
run: |
PCT=$(awk -F: '/^LF:/{lf+=$2}/^LH:/{lh+=$2}END{printf "%.0f", 100*lh/(lf?lf:1)}' lcov.info)
COLOR=$([ "$PCT" -ge 90 ] && echo 44cc11 || ([ "$PCT" -ge 75 ] && echo 97ca00 || echo dfb317))
printf '<svg xmlns="http://www.w3.org/2000/svg" width="108" height="20"><rect width="108" height="20" rx="3" fill="#%s"/><text x="54" y="14" font-size="11" font-family="Verdana,sans-serif" fill="#fff" text-anchor="middle">coverage %s%%</text></svg>\n' "$COLOR" "$PCT" > coverage.svg
URL="https://x-access-token:${GH_TOKEN}@github.com/Red-CF/log-cardio.git"
git clone -q --depth=1 --branch=gh-pages "$URL" ghp \
|| { mkdir ghp && git -C ghp init -q -b gh-pages && git -C ghp remote add origin "$URL"; }
mv coverage.svg ghp/coverage.svg
git -C ghp -c user.name=Red-CF -c user.email=crojasfer31@gmail.com add coverage.svg
git -C ghp -c user.name=Red-CF -c user.email=crojasfer31@gmail.com commit -q -m "coverage badge: $PCT%" --allow-empty
git -C ghp push -q origin gh-pages
test:
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo test
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo build --release
- name: also build macOS Intel (runner is ARM)
if: runner.os == 'macOS'
run: |
rustup target add x86_64-apple-darwin
cargo build --release --target x86_64-apple-darwin
mv target/x86_64-apple-darwin/release/cardio cardio-macOS-X64
- name: stage named binary
shell: bash
run: |
EXT=$([ "$RUNNER_OS" = "Windows" ] && echo .exe || echo "")
mv "target/release/cardio$EXT" "cardio-$RUNNER_OS-$RUNNER_ARCH$EXT"
- uses: actions/upload-artifact@v7
with:
name: cardio-${{ runner.os }}
path: cardio-*
version:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: does this PR touch shippable code?
id: scope
env:
GH_TOKEN: ${{ github.token }}
run: |
# the PR's actual file list (shallow clone: no merge base locally)
FILES=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" --paginate --jq '.[].filename') \
|| { echo "::error::could not list PR files — skipping the gate this way would be fail-open; retry the check"; exit 1; }
echo "$FILES"
if echo "$FILES" | grep -qE '^(src/|Cargo\.(toml|lock)$)'; then
echo "code=true" >> "$GITHUB_OUTPUT"
else
echo "PR touches no shippable code; version gate skipped."
echo "code=false" >> "$GITHUB_OUTPUT"
fi
- if: steps.scope.outputs.code == 'true'
name: version must be new to crates.io
run: |
V=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)
PUBLISHED=""
# retry: a single bad response from the registry mustn't lock merging
for i in 1 2 3; do
PUBLISHED=$(curl -sfL -A "log-cardio-ci" "https://crates.io/api/v1/crates/log-cardio/versions" | jq -r --arg v "$V" '[.versions[].num | select(. == $v)] | length' 2>/dev/null) || true
[ -n "$PUBLISHED" ] && break
sleep 3
done
[ -n "$PUBLISHED" ] || { echo "::error::registry query failed after 3 tries — retry the check"; exit 1; }
if [ "$PUBLISHED" != "0" ]; then
echo "::error::log-cardio $V is already on crates.io — bump version in Cargo.toml before merging"
exit 1
fi
release:
needs: [test, build, coverage, lint, audit]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- id: decide
name: release only if this version is new to crates.io
run: |
V=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)
KNOWN=""
# retry: a single bad response from the registry mustn't kill the run
for i in 1 2 3; do
KNOWN=$(curl -sfL -A "log-cardio-ci" "https://crates.io/api/v1/crates/log-cardio/versions" | jq -r --arg v "$V" '[.versions[].num | select(. == $v)] | length' 2>/dev/null) || true
[ -n "$KNOWN" ] && break
sleep 3
done
# fail loud, not silent: a flaky registry must skip NOTHING (a
# release that "went green" without publishing would be missed);
# the decide step is free to re-run manually, cargo publish alone
# guards against a true double publish
[ -n "$KNOWN" ] || { echo "::error::registry query failed after 3 tries — re-run the release job"; exit 1; }
if [ "$KNOWN" != "0" ]; then
echo "Already-released version $V: no publish (no-release merge)."
echo "release=false" >> "$GITHUB_OUTPUT"
else
echo "release=true" >> "$GITHUB_OUTPUT"
fi
- name: cargo publish
if: steps.decide.outputs.release == 'true'
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish
- name: tag + GitHub release
if: steps.decide.outputs.release == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
V=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)
git config user.name "Red-CF"
git config user.email "crojasfer31@gmail.com"
git tag "v$V"
git push origin "v$V"
gh release create "v$V" --generate-notes
- uses: actions/download-artifact@v8
if: steps.decide.outputs.release == 'true'
with:
path: bins
merge-multiple: true
- name: attach native binaries
if: steps.decide.outputs.release == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
V=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)
gh release upload "v$V" bins/cardio-*
bench:
if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo build --release
- name: install hyperfine
run: sudo apt-get update -qq && sudo apt-get install -y -qq hyperfine
- name: benchmark every mode on a 1M-line synthetic log
run: BENCH_JSON=bench.json ./bench.sh
- name: build badge + results page
run: |
python3 - <<'PY'
import json, datetime, os
data = json.load(open("bench.json"))["results"]
def label(cmd):
if cmd.startswith("/bin/cat"): return "cat baseline"
for pat, lab in [("--raw --trace", "trace (raw)"), ("--raw", "raw"),
("--stats", "stats"), ("--query", "query")]:
if pat in cmd: return lab
return "lean (default)"
rows = []
lean_ms = None
for r in data:
ms = r["median"] * 1000
rows.append((label(r["command"]), ms, "s" if ms >= 1000 else "ms",
r["median"]))
if label(r["command"]).startswith("lean"):
lean_ms = ms
lean_txt = f"{lean_ms/1000:.2f}" if lean_ms else "n/a"
txt = f"bench lean {lean_txt}/1M"
w = 12 + len(txt) * 6
open("bench.svg", "w").write(
f'<svg xmlns="http://www.w3.org/2000/svg" width="{w}" height="20">'
f'<rect width="{w}" height="20" rx="3" fill="#007ec6"/>'
f'<text x="{w/2}" y="14" font-size="11" font-family="Verdana,sans-serif" '
f'fill="#fff" text-anchor="middle">{txt}</text></svg>')
now = datetime.datetime.now(datetime.timezone.utc).isoformat(timespec="seconds")
body = "\n".join(
f"<tr><td>{lab}</td><td>{ms/1000:.3f} s</td></tr>" for lab, ms, _, _ in rows)
open("bench.html", "w").write(
"<!doctype html><meta charset='utf-8'><title>cardio bench</title>"
"<body><h1>cardio benchmark — 1M-line synthetic log</h1>"
f"<p>runner: {os.environ.get('ImageOS','unknown')} {os.environ.get('ImageVersion','')} "
f"| commit: {os.environ.get('GITHUB_SHA','')[:12]} | {now} | median of ≥3 runs</p>"
"<table border='1' cellpadding='4'><tr><th>mode</th><th>median / 1M lines</th></tr>"
f"{body}</table><p>Regenerated by CI after every release; trigger by hand with "
"workflow_dispatch. Local numbers on your hardware: ./bench.sh</p></body>")
PY
- name: publish gh-pages bench results
env:
GH_TOKEN: ${{ github.token }}
run: |
URL="https://x-access-token:${GH_TOKEN}@github.com/Red-CF/log-cardio.git"
git clone -q --depth=1 --branch=gh-pages "$URL" ghp || { mkdir ghp && git -C ghp init -q -b gh-pages && git -C ghp remote add origin "$URL"; }
mv bench.svg bench.html bench.json ghp/
git -C ghp -c user.name=Red-CF -c user.email=crojasfer31@gmail.com add bench.svg bench.html bench.json
git -C ghp -c user.name=Red-CF -c user.email=crojasfer31@gmail.com commit -q -m "bench results: ${GITHUB_SHA:0:12}" --allow-empty
git -C ghp push -q origin gh-pages