Skip to content

Commit c5c170c

Browse files
committed
fix(ci): post report on CI
1 parent 922f1c2 commit c5c170c

File tree

7 files changed

+149
-3
lines changed

7 files changed

+149
-3
lines changed

.github/workflows/prover.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ jobs:
2222
timeout-minutes: 120
2323
permissions:
2424
contents: read
25+
pull-requests: write
2526
steps:
2627
- name: Checkout repository
2728
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
@@ -48,7 +49,22 @@ jobs:
4849
- name: Check SP1 toolchain
4950
run: cargo prove --version
5051

52+
- name: Run prover-perf and post PR comment
53+
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
54+
env:
55+
ZKVM_MOCK: "1"
56+
ZKVM_PROFILING: "1"
57+
run: |
58+
cargo run --release -p strata-asm-prover-perf -- \
59+
--post-to-gh \
60+
--github-token "${{ secrets.GITHUB_TOKEN }}" \
61+
--pr-number "${{ github.event.pull_request.number }}" \
62+
--commit-hash "${{ github.sha }}" \
63+
--github-repo "${{ github.repository }}" \
64+
--programs asm-stf
65+
5166
- name: Run prover-perf
67+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == true
5268
env:
5369
ZKVM_MOCK: "1"
5470
ZKVM_PROFILING: "1"

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/prover-perf/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ zkaleido = { workspace = true, features = ["perf"] }
2020
anyhow.workspace = true
2121
clap.workspace = true
2222
num-format = "0.4.4"
23+
reqwest = { version = "0.12.28", default-features = false, features = [
24+
"blocking",
25+
"json",
26+
"rustls-tls",
27+
] }
28+
serde_json.workspace = true
2329
sp1-sdk = "5.2.1"
2430
zkaleido-sp1-host = { git = "https://github.com/alpenlabs/zkaleido", tag = "v0.1.0-alpha-rc21", features = [
2531
"perf",

bin/prover-perf/src/args.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,36 @@
1+
use std::env;
2+
13
use clap::Parser;
24

35
use crate::programs::GuestProgram;
46

7+
fn default_github_repo() -> String {
8+
env::var("GITHUB_REPOSITORY").unwrap_or_else(|_| "alpenlabs/asm".to_string())
9+
}
10+
511
/// Evaluate SP1 prover performance for ASM programs.
612
#[derive(Debug, Clone, Parser)]
713
pub(crate) struct EvalArgs {
14+
/// Whether to post the results as a GitHub PR comment.
15+
#[arg(long, default_value_t = false)]
16+
pub post_to_gh: bool,
17+
18+
/// GitHub token used to authenticate API requests.
19+
#[arg(long, default_value_t = String::new())]
20+
pub github_token: String,
21+
22+
/// Pull request number to post comment to.
23+
#[arg(long, default_value_t = String::new())]
24+
pub pr_number: String,
25+
26+
/// Commit hash shown in the generated report header.
27+
#[arg(long, default_value = "local_commit")]
28+
pub commit_hash: String,
29+
30+
/// GitHub repository in `owner/repo` format.
31+
#[arg(long, default_value_t = default_github_repo())]
32+
pub github_repo: String,
33+
834
/// Programs to run. Supports comma-delimited and repeated values, e.g.
935
/// `--programs asm-stf` or `--programs asm-stf,asm-stf`.
1036
#[arg(long)]

bin/prover-perf/src/format.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
use num_format::{Locale, ToFormattedString};
22
use zkaleido::PerformanceReport;
33

4+
use crate::args::EvalArgs;
5+
46
/// Returns a formatted header for the performance report.
5-
pub(crate) fn format_header() -> String {
6-
"*Local execution*".to_string()
7+
pub(crate) fn format_header(args: &EvalArgs) -> String {
8+
if args.post_to_gh {
9+
let short_commit: String = args.commit_hash.chars().take(8).collect();
10+
format!("*Commit*: {short_commit}")
11+
} else {
12+
"*Local execution*".to_string()
13+
}
714
}
815

916
/// Returns formatted results for the [`PerformanceReport`]s as a table.

bin/prover-perf/src/github.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use anyhow::{anyhow, bail, Result};
2+
use reqwest::blocking::{Client, RequestBuilder};
3+
use serde_json::json;
4+
5+
use crate::args::EvalArgs;
6+
7+
/// Posts the report to the configured GitHub PR.
8+
///
9+
/// Updates an existing previous comment by `github-actions[bot]` (matching the
10+
/// prover output marker) if present, otherwise posts a new one.
11+
pub(crate) fn post_to_github_pr(args: &EvalArgs, message: &str) -> Result<()> {
12+
if args.github_token.trim().is_empty() {
13+
bail!("--github-token is required when --post-to-gh is set");
14+
}
15+
if args.pr_number.trim().is_empty() {
16+
bail!("--pr-number is required when --post-to-gh is set");
17+
}
18+
19+
let client = Client::new();
20+
let comments_url = format!(
21+
"https://api.github.com/repos/{}/issues/{}/comments",
22+
args.github_repo, args.pr_number
23+
);
24+
25+
let comments_response =
26+
set_github_headers(client.get(&comments_url), &args.github_token).send()?;
27+
if !comments_response.status().is_success() {
28+
let status = comments_response.status();
29+
let body = comments_response.text().unwrap_or_default();
30+
bail!("failed to fetch PR comments ({status}): {body}");
31+
}
32+
33+
let comments: Vec<serde_json::Value> = comments_response
34+
.json()
35+
.map_err(|e| anyhow!("failed to decode PR comments response: {e}"))?;
36+
37+
let bot_comment = comments.iter().find(|comment| {
38+
let is_actions_bot = comment["user"]["login"].as_str() == Some("github-actions[bot]");
39+
let has_perf_marker = comment["body"]
40+
.as_str()
41+
.map(|body| body.contains("SP1 Execution Results"))
42+
.unwrap_or(false);
43+
is_actions_bot && has_perf_marker
44+
});
45+
46+
let request = if let Some(existing_comment) = bot_comment {
47+
let comment_url = existing_comment["url"]
48+
.as_str()
49+
.ok_or_else(|| anyhow!("existing bot comment did not include url field"))?;
50+
client.patch(comment_url)
51+
} else {
52+
client.post(&comments_url)
53+
};
54+
55+
let response = set_github_headers(request, &args.github_token)
56+
.json(&json!({ "body": message }))
57+
.send()?;
58+
59+
if !response.status().is_success() {
60+
let status = response.status();
61+
let body = response.text().unwrap_or_default();
62+
bail!("failed to post/update PR comment ({status}): {body}");
63+
}
64+
65+
Ok(())
66+
}
67+
68+
fn set_github_headers(builder: RequestBuilder, token: &str) -> RequestBuilder {
69+
builder
70+
.header("Authorization", format!("Bearer {token}"))
71+
.header("X-GitHub-Api-Version", "2022-11-28")
72+
.header("User-Agent", "strata-asm-prover-perf")
73+
}
74+
75+
pub(crate) fn format_github_message(results_text: &[String]) -> String {
76+
let mut message = String::new();
77+
for line in results_text {
78+
message.push_str(&line.replace('*', "**"));
79+
message.push('\n');
80+
}
81+
message
82+
}

bin/prover-perf/src/main.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,30 @@ use sp1_sdk::utils::setup_logger;
88

99
mod args;
1010
mod format;
11+
mod github;
1112
mod programs;
1213

1314
use args::{parse_programs, EvalArgs};
1415
use format::{format_header, format_results};
16+
use github::{format_github_message, post_to_github_pr};
1517

1618
fn main() -> Result<()> {
1719
setup_logger();
1820
let args = EvalArgs::parse();
1921

2022
let programs = parse_programs(&args.programs).map_err(anyhow::Error::msg)?;
2123

22-
let mut results_text = vec![format_header()];
24+
let mut results_text = vec![format_header(&args)];
2325
let sp1_reports = programs::run_sp1_programs(&programs);
2426
results_text.push(format_results(&sp1_reports, "SP1".to_string()));
2527

2628
println!("{}", results_text.join("\n"));
2729

30+
if args.post_to_gh {
31+
let message = format_github_message(&results_text);
32+
post_to_github_pr(&args, &message)?;
33+
}
34+
2835
if !sp1_reports.iter().all(|r| r.success) {
2936
process::exit(1);
3037
}

0 commit comments

Comments
 (0)