Skip to content

Commit 305edaf

Browse files
committed
run
1 parent c290bab commit 305edaf

File tree

8 files changed

+194
-19
lines changed

8 files changed

+194
-19
lines changed

lib/bencher_context/src/client/mod.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ mod platform;
77
pub use platform::Fingerprint;
88
use platform::OperatingSystem;
99

10-
use crate::ReportContext;
10+
use crate::{ContextKey, ReportContext};
1111

1212
const ROOT: &str = "root";
1313

1414
impl ReportContext {
15-
pub fn new() -> Self {
15+
pub fn current() -> Self {
1616
get_context()
1717
}
1818

@@ -42,28 +42,31 @@ fn git_context(context: &mut ReportContext) {
4242
};
4343

4444
if let Some(repo_name) = repo_name(&repo) {
45-
context.insert("/repo/name", repo_name);
45+
context.insert(ContextKey::REPO_NAME, repo_name);
4646
}
4747

4848
if let Some(root_commit) = repo_hash(&repo) {
49-
context.insert("/repo/hash", root_commit);
49+
context.insert(ContextKey::REPO_HASH, root_commit);
5050
}
5151

5252
if let Some((branch_ref, branch_ref_name)) = branch_ref(&repo) {
53-
context.insert("/branch/ref", branch_ref);
54-
context.insert("/branch/ref/name", branch_ref_name);
53+
context.insert(ContextKey::BRANCH_REF, branch_ref);
54+
context.insert(ContextKey::BRANCH_REF_NAME, branch_ref_name);
5555
}
5656

5757
if let Some(hash) = branch_hash(&repo) {
58-
context.insert("/branch/hash", hash);
58+
context.insert(ContextKey::BRANCH_HASH, hash);
5959
}
6060
}
6161

6262
fn platform_context(context: &mut ReportContext) {
63-
context.insert("/testbed/os", OperatingSystem::current().to_string());
63+
context.insert(
64+
ContextKey::TESTBED_OS,
65+
OperatingSystem::current().to_string(),
66+
);
6467

6568
if let Some(fingerprint) = Fingerprint::current() {
66-
context.insert("/testbed/fingerprint", fingerprint.to_string());
69+
context.insert(ContextKey::TESTBED_FINGERPRINT, fingerprint.to_string());
6770
}
6871
}
6972

@@ -96,12 +99,14 @@ fn repo_hash(repo: &Repository) -> Option<String> {
9699
}
97100

98101
fn branch_ref(repo: &Repository) -> Option<(String, String)> {
99-
repo.head().ok()?.referent_name().map(|name| {
100-
(
102+
if let Ok(Some(name)) = repo.head_name() {
103+
Some((
101104
String::from_utf8_lossy(name.as_bstr()).to_string(),
102105
String::from_utf8_lossy(name.shorten()).to_string(),
103-
)
104-
})
106+
))
107+
} else {
108+
None
109+
}
105110
}
106111

107112
fn branch_hash(repo: &Repository) -> Option<String> {

lib/bencher_context/src/lib.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,48 @@ pub use client::Fingerprint;
1414
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1515
#[cfg_attr(feature = "schema", derive(JsonSchema))]
1616
pub struct ReportContext(pub HashMap<String, String>);
17+
18+
#[allow(clippy::multiple_inherent_impl)]
19+
impl ReportContext {
20+
pub fn repo_name(&self) -> Option<&str> {
21+
self.0.get(ContextKey::REPO_NAME).map(String::as_str)
22+
}
23+
24+
pub fn repo_hash(&self) -> Option<&str> {
25+
self.0.get(ContextKey::REPO_HASH).map(String::as_str)
26+
}
27+
28+
pub fn branch_ref(&self) -> Option<&str> {
29+
self.0.get(ContextKey::BRANCH_REF).map(String::as_str)
30+
}
31+
32+
pub fn branch_ref_name(&self) -> Option<&str> {
33+
self.0.get(ContextKey::BRANCH_REF_NAME).map(String::as_str)
34+
}
35+
36+
pub fn branch_hash(&self) -> Option<&str> {
37+
self.0.get(ContextKey::BRANCH_HASH).map(String::as_str)
38+
}
39+
40+
pub fn testbed_os(&self) -> Option<&str> {
41+
self.0.get(ContextKey::TESTBED_OS).map(String::as_str)
42+
}
43+
44+
pub fn testbed_fingerprint(&self) -> Option<&str> {
45+
self.0
46+
.get(ContextKey::TESTBED_FINGERPRINT)
47+
.map(String::as_str)
48+
}
49+
}
50+
51+
struct ContextKey;
52+
53+
impl ContextKey {
54+
pub const REPO_NAME: &str = "/repo/name";
55+
pub const REPO_HASH: &str = "/repo/hash";
56+
pub const BRANCH_REF: &str = "/branch/ref";
57+
pub const BRANCH_REF_NAME: &str = "/branch/ref/name";
58+
pub const BRANCH_HASH: &str = "/branch/hash";
59+
pub const TESTBED_OS: &str = "/testbed/os";
60+
pub const TESTBED_FINGERPRINT: &str = "/testbed/fingerprint";
61+
}

lib/bencher_json/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub mod big_int;
2020
pub mod organization;
2121
pub mod pagination;
2222
pub mod project;
23+
pub mod run;
2324
pub mod system;
2425
pub(crate) mod typed_uuid;
2526
pub mod urlencoded;
@@ -53,6 +54,7 @@ pub use project::{
5354
threshold::{JsonNewThreshold, JsonThreshold, JsonThresholds, ThresholdUuid},
5455
JsonNewProject, JsonProject, JsonProjects, ProjectUuid,
5556
};
57+
pub use run::JsonNewRun;
5658
#[cfg(feature = "plus")]
5759
pub use system::{
5860
auth::JsonOAuth,

lib/bencher_json/src/project/branch.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ pub const START_POINT_MAX_VERSIONS: u32 = 255;
1414

1515
pub const BRANCH_MAIN_STR: &str = "main";
1616
#[allow(clippy::expect_used)]
17+
pub static DEFAULT_BRANCH: LazyLock<NameId> = LazyLock::new(|| {
18+
BRANCH_MAIN_STR
19+
.parse()
20+
.expect("Failed to parse branch name.")
21+
});
22+
#[allow(clippy::expect_used)]
1723
static BRANCH_MAIN: LazyLock<BranchName> = LazyLock::new(|| {
1824
BRANCH_MAIN_STR
1925
.parse()

lib/bencher_json/src/project/testbed.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::fmt;
22
use std::sync::LazyLock;
33

4-
use bencher_valid::{DateTime, ResourceName, Slug};
4+
use bencher_valid::{DateTime, NameId, ResourceName, Slug};
55
#[cfg(feature = "schema")]
66
use schemars::JsonSchema;
77
use serde::{Deserialize, Serialize};
@@ -10,6 +10,12 @@ use crate::ProjectUuid;
1010

1111
pub const TESTBED_LOCALHOST_STR: &str = "localhost";
1212
#[allow(clippy::expect_used)]
13+
pub static DEFAULT_TESTBED: LazyLock<NameId> = LazyLock::new(|| {
14+
TESTBED_LOCALHOST_STR
15+
.parse()
16+
.expect("Failed to parse testbed name.")
17+
});
18+
#[allow(clippy::expect_used)]
1319
static TESTBED_LOCALHOST: LazyLock<ResourceName> = LazyLock::new(|| {
1420
TESTBED_LOCALHOST_STR
1521
.parse()

lib/bencher_json/src/run.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use bencher_context::ReportContext;
2+
use bencher_valid::{DateTime, GitHash, ResourceId};
3+
#[cfg(feature = "schema")]
4+
use schemars::JsonSchema;
5+
use serde::{Deserialize, Serialize};
6+
7+
use crate::{
8+
project::{
9+
branch::{JsonUpdateStartPoint, DEFAULT_BRANCH},
10+
report::{JsonReportSettings, JsonReportThresholds},
11+
testbed::DEFAULT_TESTBED,
12+
},
13+
JsonNewReport, NameId,
14+
};
15+
16+
#[derive(Debug, Serialize, Deserialize)]
17+
#[cfg_attr(feature = "schema", derive(JsonSchema))]
18+
pub struct JsonNewRun {
19+
/// Organization UUID or slug.
20+
/// If the organization is not provided, it will be created.
21+
pub organization: Option<ResourceId>,
22+
/// Project UUID, slug, or name.
23+
/// If the project is not provided or does not exist, it will be created.
24+
/// If a name is provided, the `organization` field must also be provided.
25+
pub project: Option<NameId>,
26+
/// Branch UUID, slug, or name.
27+
/// If the branch is not provided or does not exist, it will be created.
28+
pub branch: Option<NameId>,
29+
/// Full `git` commit hash.
30+
/// All reports with the same `git` commit hash will be considered part of the same branch version.
31+
/// This can be useful for tracking the performance of a specific commit across multiple testbeds.
32+
pub hash: Option<GitHash>,
33+
/// The start point for the report branch.
34+
/// If the branch does not exist, the start point will be used to create a new branch.
35+
/// If the branch already exists and the start point is not provided, the current branch will be used.
36+
/// If the branch already exists and the start point provided is different, a new branch head will be created from the new start point.
37+
/// If a new branch or new branch head is created with a start point,
38+
/// historical branch versions from the start point branch will be shallow copied over to the new branch.
39+
/// That is, historical metrics data for the start point branch will appear in queries for the branch.
40+
/// For example, pull request branches often use their base branch as their start point branch.
41+
/// If a new branch is created, it is not kept in sync with the start point branch.
42+
pub start_point: Option<JsonUpdateStartPoint>,
43+
/// Testbed UUID, slug, or name.
44+
/// If the testbed is not provided or does not exist, it will be created.
45+
pub testbed: Option<NameId>,
46+
/// Thresholds to use for the branch, testbed, and measures in the report.
47+
/// If a threshold does not exist, it will be created.
48+
/// If a threshold exists and the model is different, it will be updated with the new model.
49+
/// If a measure name or slug is provided, the measure will be created if it does not exist.
50+
pub thresholds: Option<JsonReportThresholds>,
51+
/// Start time for the report. Must be an ISO 8601 formatted string.
52+
pub start_time: DateTime,
53+
/// End time for the report. Must be an ISO 8601 formatted string.
54+
pub end_time: DateTime,
55+
/// An array of benchmarks results.
56+
pub results: Vec<String>,
57+
/// Settings for how to handle the results.
58+
pub settings: Option<JsonReportSettings>,
59+
/// Context for the report.
60+
pub context: Option<ReportContext>,
61+
}
62+
63+
impl From<JsonNewRun> for JsonNewReport {
64+
fn from(run: JsonNewRun) -> Self {
65+
let JsonNewRun {
66+
organization: _,
67+
project: _,
68+
branch,
69+
hash,
70+
start_point,
71+
testbed,
72+
thresholds,
73+
start_time,
74+
end_time,
75+
results,
76+
settings,
77+
context,
78+
} = run;
79+
let branch = branch
80+
.or_else(|| {
81+
context
82+
.as_ref()
83+
.and_then(|ctx| ctx.branch_ref_name().and_then(|branch| branch.parse().ok()))
84+
})
85+
.unwrap_or_else(|| DEFAULT_BRANCH.clone());
86+
let hash = hash.or_else(|| {
87+
context
88+
.as_ref()
89+
.and_then(|ctx| ctx.branch_hash().and_then(|hash| hash.parse().ok()))
90+
});
91+
let testbed = testbed
92+
.or_else(|| {
93+
context
94+
.as_ref()
95+
.and_then(|ctx| ctx.testbed_os().and_then(|testbed| testbed.parse().ok()))
96+
})
97+
.unwrap_or_else(|| DEFAULT_TESTBED.clone());
98+
Self {
99+
branch,
100+
hash,
101+
start_point,
102+
testbed,
103+
thresholds,
104+
start_time,
105+
end_time,
106+
results,
107+
settings,
108+
context,
109+
}
110+
}
111+
}

services/api/src/endpoints/run/run.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bencher_json::{JsonNewReport, JsonReport};
1+
use bencher_json::{JsonNewRun, JsonReport};
22

33
use dropshot::{endpoint, HttpError, RequestContext, TypedBody};
44
use slog::Logger;
@@ -39,7 +39,7 @@ pub async fn run_options(_rqctx: RequestContext<ApiContext>) -> Result<CorsRespo
3939
pub async fn run_post(
4040
rqctx: RequestContext<ApiContext>,
4141
bearer_token: PubBearerToken,
42-
body: TypedBody<JsonNewReport>,
42+
body: TypedBody<JsonNewRun>,
4343
) -> Result<ResponseCreated<JsonReport>, HttpError> {
4444
let auth_user = AuthUser::from_pub_token(rqctx.context(), bearer_token).await?;
4545
let json = post_inner(&rqctx.log, rqctx.context(), body.into_inner(), auth_user).await?;
@@ -49,7 +49,7 @@ pub async fn run_post(
4949
async fn post_inner(
5050
log: &Logger,
5151
context: &ApiContext,
52-
json_report: JsonNewReport,
52+
json_run: JsonNewRun,
5353
auth_user: Option<AuthUser>,
5454
) -> Result<JsonReport, HttpError> {
5555
#[allow(clippy::unimplemented)]
@@ -62,5 +62,5 @@ async fn post_inner(
6262
Err(bad_request_error("pub run creation is not yet implemented"))
6363
};
6464
let auth_user = todo_pub_run_user(auth_user)?;
65-
QueryReport::create(log, context, &query_project, json_report, &auth_user).await
65+
QueryReport::create(log, context, &query_project, json_run.into(), &auth_user).await
6666
}

services/cli/src/bencher/sub/project/run/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ impl Run {
201201
average: self.average,
202202
fold: self.fold,
203203
}),
204-
context: Some(ReportContext::new().into()),
204+
context: Some(ReportContext::current().into()),
205205
}))
206206
}
207207

0 commit comments

Comments
 (0)