name: Rust CI
on:
push:
branches:
- "**" pull_request:
branches:
- "**"
permissions:
issues: write
pull-requests: write
jobs:
msrv:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Install Rust (MSRV)
uses: actions-rs/toolchain@v1
with:
toolchain: 1.78.0
override: true
- name: Build and Test
run: |
cargo build --release
cargo test
- name: Run Benchmarks
run: cargo bench --features bench
- name: Parse Benchmark Results
run: |
npm install glob
shell: bash
- name: Create Consolidated Benchmark Results
id: create_results
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const path = require('path');
const glob2 = require('glob');
const files = glob2.sync('target/criterion/**/new/estimates.json', { ignore: '**/report/**' });
const benchmarks = [];
for (const file of files) {
const estimates = JSON.parse(fs.readFileSync(file, 'utf8'));
// Split the path into parts
const normalizedFile = file.replace(/\\/g, '/');
const parts = normalizedFile.split('/');
const indexOfCriterion = parts.indexOf('criterion');
const indexOfNew = parts.indexOf('new');
// Extract the part(s) of the path between 'criterion' and 'new'
// For example: target/criterion/<group>/<test>/new/estimates.json
// becomes <group>/<test>
const relevantParts = parts.slice(indexOfCriterion + 1, indexOfNew);
const benchmarkName = relevantParts.join('/');
// Extract the mean estimate in ns
let mean_ns = estimates.mean ? estimates.mean.point_estimate : null;
if (mean_ns !== null) {
// Convert units if needed
let value = mean_ns;
let unit = 'ns';
// If ≥ 1 second
if (value >= 1e9) {
value = value / 1e9; // ns to s
unit = 's';
} else if (value >= 1e6) {
// If ≥ 1 ms but < 1 s
value = value / 1e6; // ns to ms
unit = 'ms';
}
// Round to 3 decimal places
value = parseFloat(value.toFixed(3));
benchmarks.push({
name: benchmarkName,
value: value,
unit: unit
});
}
}
fs.mkdirSync('bench_results', { recursive: true });
fs.writeFileSync('bench_results/results.json', JSON.stringify(benchmarks, null, 2));
return { benchmarks };
- name: Upload Benchmark Results
uses: actions/upload-artifact@v3
with:
name: rust-msrv-bench--${{ matrix.os }}
path: bench_results/results.json
- name: Post Benchmark Results on Pull Requests
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const results = fs.readFileSync('bench_results/results.json', 'utf8');
const benchmarks = JSON.parse(results);
// Create a Markdown table
const header = "| Benchmark Name | Value | Unit |\n|---|---|---|";
const rows = benchmarks.map(b => `| ${b.name} | ${b.value} | ${b.unit} |`).join('\n');
const table = `${header}\n${rows}`;
const comment = `### Benchmark Results (MSRV)- ${{ matrix.os }}\n${table}`;
github.rest.issues.createComment({
issue_number: context.payload.pull_request.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
- name: Display Benchmark Results in Logs
if: github.event_name != 'pull_request'
run: |
echo "### Benchmark Results"
cat bench_results/results.json
latest:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Install Latest Stable Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Build and Test
run: |
cargo build --release
cargo test
- name: Run Benchmarks
run: cargo bench --features bench
- name: Parse Benchmark Results
run: |
npm install glob
shell: bash
- name: Create Consolidated Benchmark Results
id: create_results
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const path = require('path');
const glob2 = require('glob');
const files = glob2.sync('target/criterion/**/new/estimates.json', { ignore: '**/report/**' });
const benchmarks = [];
for (const file of files) {
const estimates = JSON.parse(fs.readFileSync(file, 'utf8'));
// Split the path into parts
const normalizedFile = file.replace(/\\/g, '/');
const parts = normalizedFile.split('/');
const indexOfCriterion = parts.indexOf('criterion');
const indexOfNew = parts.indexOf('new');
const relevantParts = parts.slice(indexOfCriterion + 1, indexOfNew);
const benchmarkName = relevantParts.join('/');
let mean_ns = estimates.mean ? estimates.mean.point_estimate : null;
if (mean_ns !== null) {
let value = mean_ns;
let unit = 'ns';
if (value >= 1e9) {
value = value / 1e9; // convert ns to s
unit = 's';
} else if (value >= 1e6) {
value = value / 1e6; // convert ns to ms
unit = 'ms';
}
value = parseFloat(value.toFixed(3));
benchmarks.push({
name: benchmarkName,
value: value,
unit: unit
});
}
}
fs.mkdirSync('bench_results', { recursive: true });
fs.writeFileSync('bench_results/results.json', JSON.stringify(benchmarks, null, 2));
return { benchmarks };
- name: Upload Benchmark Results
uses: actions/upload-artifact@v3
with:
name: rust-latest-bench--${{ matrix.os }}
path: bench_results/results.json
- name: Post Benchmark Results on Pull Requests
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const results = fs.readFileSync('bench_results/results.json', 'utf8');
const benchmarks = JSON.parse(results);
const header = "| Benchmark Name | Value | Unit |\n|---|---|---|";
const rows = benchmarks.map(b => `| ${b.name} | ${b.value} | ${b.unit} |`).join('\n');
const table = `${header}\n${rows}`;
const comment = `### Benchmark Results (Latest) - ${{ matrix.os }}\n${table}`;
github.rest.issues.createComment({
issue_number: context.payload.pull_request.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
- name: Display Benchmark Results in Logs
if: github.event_name != 'pull_request'
run: |
echo "### Benchmark Results"
cat bench_results/results.json