Allow refreshing projects without existing reports

This commit is contained in:
Luke Street
2024-10-10 00:24:28 -06:00
parent f31ae74fe5
commit 6cfc9a0027
11 changed files with 160 additions and 120 deletions
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "\n SELECT\n projects.id AS \"project_id!\",\n owner AS \"owner!\",\n repo AS \"repo!\",\n name,\n short_name,\n default_version,\n platform,\n git_commit AS \"git_commit!\",\n MAX(timestamp) AS \"timestamp: chrono::NaiveDateTime\",\n JSON_GROUP_ARRAY(version ORDER BY version) AS versions\n FROM projects JOIN reports ON projects.id = reports.project_id\n WHERE timestamp = (\n SELECT MAX(timestamp)\n FROM reports\n WHERE project_id = projects.id\n )\n GROUP BY projects.id\n ORDER BY MAX(timestamp) DESC\n ",
"query": "\n SELECT\n projects.id AS \"project_id!\",\n owner AS \"owner!\",\n repo AS \"repo!\",\n name,\n short_name,\n default_version,\n platform,\n git_commit,\n MAX(timestamp) AS \"timestamp: chrono::NaiveDateTime\",\n JSON_GROUP_ARRAY(version ORDER BY version)\n FILTER (WHERE version IS NOT NULL) AS versions\n FROM projects LEFT JOIN reports ON (\n reports.project_id = projects.id\n AND reports.timestamp = (\n SELECT MAX(timestamp)\n FROM reports\n WHERE project_id = projects.id\n )\n )\n GROUP BY projects.id\n ORDER BY MAX(timestamp) DESC\n ",
"describe": {
"columns": [
{
@@ -39,7 +39,7 @@
"type_info": "Text"
},
{
"name": "git_commit!",
"name": "git_commit",
"ordinal": 7,
"type_info": "Text"
},
@@ -66,9 +66,9 @@
true,
true,
true,
false,
false
true,
true
]
},
"hash": "33ef6c68364ca0148a0cec67caba762484635c8e9881fe29b7f0e75cff235b31"
"hash": "1c5ea45ed729197e046adfd9a9129de3479799b64556826043d6dd909c314429"
}
@@ -1,20 +0,0 @@
{
"db_name": "SQLite",
"query": "\n SELECT git_commit\n FROM reports\n WHERE project_id = ? AND timestamp > ?\n ORDER BY timestamp\n LIMIT 1\n ",
"describe": {
"columns": [
{
"name": "git_commit",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 2
},
"nullable": [
false
]
},
"hash": "6550bd96a82a16800fe801be26a52297907c2c3de6ea88951495175e81094343"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "\n SELECT git_commit\n FROM reports\n WHERE project_id = ? AND timestamp < ?\n ORDER BY timestamp DESC\n LIMIT 1\n ",
"describe": {
"columns": [
{
"name": "git_commit",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 2
},
"nullable": [
false
]
},
"hash": "6c9bea8b05a4bd3bdfc6f4a1381079bfc26fdd49db6e1abc215516b44d3ce183"
}
@@ -1,20 +0,0 @@
{
"db_name": "SQLite",
"query": "\n SELECT git_commit\n FROM reports\n WHERE project_id = ? AND timestamp < ?\n ORDER BY timestamp DESC\n LIMIT 1\n ",
"describe": {
"columns": [
{
"name": "git_commit",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 2
},
"nullable": [
false
]
},
"hash": "7418a824fbb682e0044774c586a14643065ae66bc008a649cf6669988c738f04"
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "\n SELECT git_commit\n FROM reports\n WHERE project_id = ? AND timestamp > ?\n ORDER BY timestamp\n LIMIT 1\n ",
"describe": {
"columns": [
{
"name": "git_commit",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 2
},
"nullable": [
false
]
},
"hash": "750fca04470de49bd9fa4cf77c19aa579d3556cf20994a618f5c04241ab65e20"
}
+9 -2
View File
@@ -1,4 +1,4 @@
use anyhow::Result;
use anyhow::{Context, Result};
use tokio_cron_scheduler::{Job, JobScheduler};
use crate::{github, AppState};
@@ -21,7 +21,14 @@ pub async fn create(state: AppState) -> Result<Scheduler> {
pub async fn refresh_projects(state: &mut AppState) -> Result<()> {
for project_info in state.db.get_projects().await? {
github::run(state, &project_info.project.owner, &project_info.project.repo, 0).await?;
github::run(state, &project_info.project.owner, &project_info.project.repo, 0)
.await
.with_context(|| {
format!(
"Failed to refresh {}/{}",
project_info.project.owner, project_info.project.repo
)
})?;
}
Ok(())
}
+64 -49
View File
@@ -345,49 +345,53 @@ impl Database {
})
.collect::<Vec<_>>()
};
let Some(first_report) = reports.first() else {
return Ok(None);
};
// Fetch previous and next commits
let timestamp = first_report.timestamp.and_utc();
let prev_commit = sqlx::query!(
r#"
SELECT git_commit
FROM reports
WHERE project_id = ? AND timestamp < ?
ORDER BY timestamp DESC
LIMIT 1
"#,
project_id,
timestamp,
)
.fetch_optional(&mut *conn)
.await?
.map(|row| row.git_commit);
let next_commit = sqlx::query!(
r#"
SELECT git_commit
FROM reports
WHERE project_id = ? AND timestamp > ?
ORDER BY timestamp
LIMIT 1
"#,
project_id,
timestamp,
)
.fetch_optional(&mut *conn)
.await?
.map(|row| row.git_commit);
Ok(Some(ProjectInfo {
let mut info = ProjectInfo {
project,
commit: Commit {
commit: None,
report_versions: reports.iter().map(|r| r.version.clone()).collect(),
prev_commit: None,
next_commit: None,
};
if let Some(first_report) = reports.first() {
// Fetch previous and next commits
let timestamp = first_report.timestamp.and_utc();
let prev_commit = sqlx::query!(
r#"
SELECT git_commit
FROM reports
WHERE project_id = ? AND timestamp < ?
ORDER BY timestamp DESC
LIMIT 1
"#,
project_id,
timestamp,
)
.fetch_optional(&mut *conn)
.await?
.map(|row| row.git_commit);
let next_commit = sqlx::query!(
r#"
SELECT git_commit
FROM reports
WHERE project_id = ? AND timestamp > ?
ORDER BY timestamp
LIMIT 1
"#,
project_id,
timestamp,
)
.fetch_optional(&mut *conn)
.await?
.map(|row| row.git_commit);
info.commit = Some(Commit {
sha: first_report.git_commit.clone(),
timestamp: first_report.timestamp.and_utc(),
},
report_versions: reports.iter().map(|r| r.version.clone()).collect(),
prev_commit,
next_commit,
}))
});
info.prev_commit = prev_commit;
info.next_commit = next_commit;
}
Ok(Some(info))
}
pub async fn get_projects(&self) -> Result<Vec<ProjectInfo>> {
@@ -402,14 +406,17 @@ impl Database {
short_name,
default_version,
platform,
git_commit AS "git_commit!",
git_commit,
MAX(timestamp) AS "timestamp: chrono::NaiveDateTime",
JSON_GROUP_ARRAY(version ORDER BY version) AS versions
FROM projects JOIN reports ON projects.id = reports.project_id
WHERE timestamp = (
SELECT MAX(timestamp)
FROM reports
WHERE project_id = projects.id
JSON_GROUP_ARRAY(version ORDER BY version)
FILTER (WHERE version IS NOT NULL) AS versions
FROM projects LEFT JOIN reports ON (
reports.project_id = projects.id
AND reports.timestamp = (
SELECT MAX(timestamp)
FROM reports
WHERE project_id = projects.id
)
)
GROUP BY projects.id
ORDER BY MAX(timestamp) DESC
@@ -428,8 +435,16 @@ impl Database {
default_version: row.default_version,
platform: row.platform,
},
commit: Commit { sha: row.git_commit, timestamp: row.timestamp.and_utc() },
report_versions: serde_json::from_str(&row.versions).unwrap_or_default(),
commit: match (row.git_commit, row.timestamp) {
(Some(sha), Some(timestamp)) => {
Some(Commit { sha, timestamp: timestamp.and_utc() })
}
_ => None,
},
report_versions: row
.versions
.and_then(|s| serde_json::from_str(&s).ok())
.unwrap_or_default(),
prev_commit: None,
next_commit: None,
})
+20 -9
View File
@@ -77,7 +77,11 @@ impl GitHub {
pub async fn run(state: &mut AppState, owner: &str, repo: &str, stop_run_id: u64) -> Result<()> {
tracing::info!("Refreshing project {}/{}", owner, repo);
let existing = state.db.get_project_info(owner, repo, None).await?;
let existing = state
.db
.get_project_info(owner, repo, None)
.await
.context("Failed to fetch project info")?;
let repo =
state.github.client.repos(owner, repo).get().await.context("Failed to fetch repo")?;
let branch = repo.default_branch.as_deref().unwrap_or("main");
@@ -107,15 +111,22 @@ pub async fn run(state: &mut AppState, owner: &str, repo: &str, stop_run_id: u64
.status("completed")
.exclude_pull_requests(true)
.page(page)
// .per_page(10)
.send()
.await?;
if result.items.is_empty() {
break;
}
for run in result.items {
if let Some(existing) = existing.as_ref() {
if run.head_sha == existing.commit.sha {
.await;
let items = match result {
Ok(result) if result.items.is_empty() => break,
Ok(result) => result.items,
Err(octocrab::Error::GitHub {
source: GitHubError { status_code: StatusCode::NOT_FOUND, .. },
..
}) => break,
Err(e) => {
return Err(e).with_context(|| format!("Failed to fetch workflows page {}", page));
}
};
for run in items {
if let Some(commit) = existing.as_ref().and_then(|e| e.commit.as_ref()) {
if run.head_sha == commit.sha {
break 'outer;
}
}
+17 -13
View File
@@ -60,17 +60,20 @@ pub async fn get_projects(
let projects = state.db.get_projects().await?;
let mut out = projects
.iter()
.map(|p| ProjectInfoContext {
id: p.project.id,
path: format!("/{}/{}", p.project.owner, p.project.repo),
owner: p.project.owner.clone(),
repo: p.project.repo.clone(),
name: p.project.name().into_owned(),
short_name: p.project.short_name().to_owned(),
commit: p.commit.sha.clone(),
timestamp: p.commit.timestamp,
measures: Default::default(),
platform: p.project.platform.clone(),
.filter_map(|p| {
let commit = p.commit.as_ref()?;
Some(ProjectInfoContext {
id: p.project.id,
path: format!("/{}/{}", p.project.owner, p.project.repo),
owner: p.project.owner.clone(),
repo: p.project.repo.clone(),
name: p.project.name().into_owned(),
short_name: p.project.short_name().to_owned(),
commit: commit.sha.clone(),
timestamp: commit.timestamp,
measures: Default::default(),
platform: p.project.platform.clone(),
})
})
.collect::<Vec<_>>();
@@ -85,14 +88,15 @@ pub async fn get_projects(
let Some(version) = info.default_version() else {
return (info, Err(anyhow!("No report version found")));
};
let commit = info.commit.as_ref().unwrap();
let report = state
.db
.get_report(&info.project.owner, &info.project.repo, &info.commit.sha, version)
.get_report(&info.project.owner, &info.project.repo, &commit.sha, version)
.await
.with_context(|| {
format!(
"Failed to fetch report for {}/{} sha {} version {}",
info.project.owner, info.project.repo, info.commit.sha, version
info.project.owner, info.project.repo, commit.sha, version
)
});
(info, report)
+4 -1
View File
@@ -208,6 +208,9 @@ pub async fn get_report(
else {
return Err(AppError::Status(StatusCode::NOT_FOUND));
};
let Some(commit) = project_info.commit.as_ref() else {
return Err(AppError::Status(StatusCode::NOT_FOUND));
};
let version = if let Some(version) = &params.version {
if version.eq_ignore_ascii_case("default") {
project_info.default_version().ok_or(AppError::Status(StatusCode::NOT_FOUND))?
@@ -220,7 +223,7 @@ pub async fn get_report(
return Err(AppError::Status(StatusCode::NOT_FOUND));
};
let Some(report) =
state.db.get_report(&params.owner, &params.repo, &project_info.commit.sha, version).await?
state.db.get_report(&params.owner, &params.repo, &commit.sha, version).await?
else {
return Err(AppError::Status(StatusCode::NOT_FOUND));
};
+1 -1
View File
@@ -34,7 +34,7 @@ impl Project {
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
pub struct ProjectInfo {
pub project: Project,
pub commit: Commit,
pub commit: Option<Commit>,
pub report_versions: Vec<String>,
pub prev_commit: Option<String>,
pub next_commit: Option<String>,