mirror of
https://codeberg.org/Toasterson/solstice-ci.git
synced 2026-04-10 13:20:41 +00:00
chore(format): Format code
Signed-off-by: Till Wegmueller <toasterson@gmail.com>
This commit is contained in:
parent
c0a7f7e3f2
commit
d3841462cf
4 changed files with 124 additions and 103 deletions
|
|
@ -133,7 +133,10 @@ async fn main() -> Result<()> {
|
|||
let base_url = resolve_logs_base_url(logs_base_url)?;
|
||||
cmd_logs(&base_url, &job_id, category.as_deref()).await?;
|
||||
}
|
||||
Commands::Tui { logs_base_url, repo } => {
|
||||
Commands::Tui {
|
||||
logs_base_url,
|
||||
repo,
|
||||
} => {
|
||||
let base_url = resolve_logs_base_url(logs_base_url)?;
|
||||
let repo = resolve_repo_url(repo);
|
||||
run_tui(&base_url, repo).await?;
|
||||
|
|
@ -170,11 +173,7 @@ fn detect_git_remote() -> Option<String> {
|
|||
return None;
|
||||
}
|
||||
let s = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
if s.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(s)
|
||||
}
|
||||
if s.is_empty() { None } else { Some(s) }
|
||||
}
|
||||
|
||||
fn config_path() -> Result<PathBuf> {
|
||||
|
|
@ -185,7 +184,9 @@ fn config_path() -> Result<PathBuf> {
|
|||
} else if let Ok(home) = std::env::var("USERPROFILE") {
|
||||
PathBuf::from(home).join(".config")
|
||||
} else {
|
||||
return Err(miette::miette!("Unable to determine home directory for config storage"));
|
||||
return Err(miette::miette!(
|
||||
"Unable to determine home directory for config storage"
|
||||
));
|
||||
};
|
||||
Ok(base.join("solstice").join("ciadm.conf"))
|
||||
}
|
||||
|
|
@ -212,7 +213,11 @@ fn load_logs_base_url() -> Result<Option<String>> {
|
|||
let Some(node) = doc.get("logs_base_url") else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(value) = node.entries().first().and_then(|entry| entry.value().as_string()) else {
|
||||
let Some(value) = node
|
||||
.entries()
|
||||
.first()
|
||||
.and_then(|entry| entry.value().as_string())
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
if value.trim().is_empty() {
|
||||
|
|
@ -222,8 +227,7 @@ fn load_logs_base_url() -> Result<Option<String>> {
|
|||
}
|
||||
|
||||
async fn cmd_jobs(base_url: &str, repo: Option<&str>) -> Result<()> {
|
||||
let client = LogsClient::new(base_url)
|
||||
.map_err(|err| miette::Report::msg(err.to_string()))?;
|
||||
let client = LogsClient::new(base_url).map_err(|err| miette::Report::msg(err.to_string()))?;
|
||||
let groups = client
|
||||
.list_jobs()
|
||||
.await
|
||||
|
|
@ -254,8 +258,7 @@ async fn cmd_jobs(base_url: &str, repo: Option<&str>) -> Result<()> {
|
|||
}
|
||||
|
||||
async fn cmd_logs(base_url: &str, job_id: &str, category: Option<&str>) -> Result<()> {
|
||||
let client = LogsClient::new(base_url)
|
||||
.map_err(|err| miette::Report::msg(err.to_string()))?;
|
||||
let client = LogsClient::new(base_url).map_err(|err| miette::Report::msg(err.to_string()))?;
|
||||
let request_id = uuid::Uuid::parse_str(job_id).into_diagnostic()?;
|
||||
let text = if let Some(cat) = category {
|
||||
client
|
||||
|
|
@ -324,7 +327,9 @@ fn setup_terminal() -> Result<Terminal<ratatui::backend::CrosstermBackend<Stdout
|
|||
Terminal::new(backend).into_diagnostic()
|
||||
}
|
||||
|
||||
fn restore_terminal(mut terminal: Terminal<ratatui::backend::CrosstermBackend<Stdout>>) -> Result<()> {
|
||||
fn restore_terminal(
|
||||
mut terminal: Terminal<ratatui::backend::CrosstermBackend<Stdout>>,
|
||||
) -> Result<()> {
|
||||
terminal::disable_raw_mode().into_diagnostic()?;
|
||||
execute!(terminal.backend_mut(), LeaveAlternateScreen).into_diagnostic()?;
|
||||
terminal.show_cursor().into_diagnostic()?;
|
||||
|
|
@ -333,10 +338,7 @@ fn restore_terminal(mut terminal: Terminal<ratatui::backend::CrosstermBackend<St
|
|||
|
||||
fn filter_groups_by_repo(groups: Vec<JobGroup>, repo: Option<&str>) -> Vec<JobGroup> {
|
||||
if let Some(repo) = repo {
|
||||
groups
|
||||
.into_iter()
|
||||
.filter(|g| g.repo_url == repo)
|
||||
.collect()
|
||||
groups.into_iter().filter(|g| g.repo_url == repo).collect()
|
||||
} else {
|
||||
groups
|
||||
}
|
||||
|
|
@ -434,20 +436,14 @@ impl TuiApp {
|
|||
.map_err(|err| miette::Report::msg(err.to_string()))?;
|
||||
let mut repos_map: BTreeMap<String, Vec<JobEntry>> = BTreeMap::new();
|
||||
for group in groups {
|
||||
let entries = group
|
||||
.jobs
|
||||
.into_iter()
|
||||
.map(|job| JobEntry {
|
||||
let entries = group.jobs.into_iter().map(|job| JobEntry {
|
||||
request_id: job.request_id,
|
||||
commit_sha: group.commit_sha.clone(),
|
||||
state: job.state,
|
||||
runs_on: job.runs_on,
|
||||
updated_at: job.updated_at,
|
||||
});
|
||||
repos_map
|
||||
.entry(group.repo_url)
|
||||
.or_default()
|
||||
.extend(entries);
|
||||
repos_map.entry(group.repo_url).or_default().extend(entries);
|
||||
}
|
||||
let mut repos: Vec<String> = repos_map.keys().cloned().collect();
|
||||
repos.sort();
|
||||
|
|
@ -488,20 +484,13 @@ impl TuiApp {
|
|||
};
|
||||
match self.client.list_log_categories(job.request_id).await {
|
||||
Ok(categories) => {
|
||||
self.logs_categories = categories
|
||||
.into_iter()
|
||||
.map(|c| c.category)
|
||||
.collect();
|
||||
self.logs_categories = categories.into_iter().map(|c| c.category).collect();
|
||||
if self.logs_categories.is_empty() {
|
||||
self.logs_text = "No logs available.".to_string();
|
||||
self.logs_category = None;
|
||||
self.selected_category = 0;
|
||||
} else {
|
||||
if let Some(idx) = self
|
||||
.logs_categories
|
||||
.iter()
|
||||
.position(|c| c == "default")
|
||||
{
|
||||
if let Some(idx) = self.logs_categories.iter().position(|c| c == "default") {
|
||||
self.selected_category = idx;
|
||||
} else if self.selected_category >= self.logs_categories.len() {
|
||||
self.selected_category = 0;
|
||||
|
|
@ -624,7 +613,11 @@ impl TuiApp {
|
|||
let size = frame.area();
|
||||
let layout = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Length(3), Constraint::Min(5), Constraint::Length(2)])
|
||||
.constraints([
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(5),
|
||||
Constraint::Length(2),
|
||||
])
|
||||
.split(size);
|
||||
self.draw_header(frame, layout[0]);
|
||||
self.draw_body(frame, layout[1]);
|
||||
|
|
@ -796,9 +789,7 @@ fn parse_sgr_params(bytes: &[u8]) -> Vec<u16> {
|
|||
return Vec::new();
|
||||
}
|
||||
let s = String::from_utf8_lossy(bytes);
|
||||
s.split(';')
|
||||
.filter_map(|p| p.parse::<u16>().ok())
|
||||
.collect()
|
||||
s.split(';').filter_map(|p| p.parse::<u16>().ok()).collect()
|
||||
}
|
||||
|
||||
fn apply_sgr(style: &mut Style, params: &[u16]) {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,11 @@ struct Opts {
|
|||
hookdeck_signing_secret: Option<String>,
|
||||
|
||||
/// GitHub API base (e.g., https://api.github.com)
|
||||
#[arg(long, env = "GITHUB_API_BASE", default_value = "https://api.github.com")]
|
||||
#[arg(
|
||||
long,
|
||||
env = "GITHUB_API_BASE",
|
||||
default_value = "https://api.github.com"
|
||||
)]
|
||||
github_api_base: String,
|
||||
|
||||
/// GitHub App ID
|
||||
|
|
@ -186,7 +190,9 @@ async fn main() -> Result<()> {
|
|||
.hookdeck_signing_secret
|
||||
.or_else(|| std::env::var("HOOKDECK_SECRET").ok());
|
||||
if webhook_secret.is_none() && hookdeck_signing_secret.is_none() {
|
||||
warn!("GITHUB_WEBHOOK_SECRET and HOOKDECK_SIGNING_SECRET are not set — accepting webhooks without signature validation (dev mode)");
|
||||
warn!(
|
||||
"GITHUB_WEBHOOK_SECRET and HOOKDECK_SIGNING_SECRET are not set — accepting webhooks without signature validation (dev mode)"
|
||||
);
|
||||
}
|
||||
|
||||
let app_key_pem = match (&opts.app_key_pem, &opts.app_key_path) {
|
||||
|
|
@ -296,7 +302,9 @@ async fn get_installation_token(state: &AppState, installation_id: u64) -> Resul
|
|||
return Ok(None);
|
||||
}
|
||||
let v: serde_json::Value = resp.json().await.into_diagnostic()?;
|
||||
Ok(v.get("token").and_then(|t| t.as_str()).map(|s| s.to_string()))
|
||||
Ok(v.get("token")
|
||||
.and_then(|t| t.as_str())
|
||||
.map(|s| s.to_string()))
|
||||
}
|
||||
|
||||
async fn get_installation_id_for_repo(
|
||||
|
|
@ -487,15 +495,13 @@ async fn handle_webhook(
|
|||
Ok(extract) => handle_pull_request(state, extract.payload).await,
|
||||
Err(err) => map_webhook_error(err),
|
||||
},
|
||||
"ping" => match webhook::verify_signatures(
|
||||
&headers,
|
||||
body.as_ref(),
|
||||
&checks,
|
||||
SignaturePolicy::Any,
|
||||
) {
|
||||
"ping" => {
|
||||
match webhook::verify_signatures(&headers, body.as_ref(), &checks, SignaturePolicy::Any)
|
||||
{
|
||||
Ok(_) => StatusCode::OK,
|
||||
Err(err) => map_webhook_error(WebhookError::Signature(err)),
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => StatusCode::NO_CONTENT,
|
||||
}
|
||||
}
|
||||
|
|
@ -1290,16 +1296,19 @@ async fn handle_job_result(state: &AppState, jobres: &common::messages::JobResul
|
|||
}
|
||||
}
|
||||
|
||||
let (owner, repo) = match (
|
||||
jobres.repo_owner.as_ref(),
|
||||
jobres.repo_name.as_ref(),
|
||||
) {
|
||||
let (owner, repo) = match (jobres.repo_owner.as_ref(), jobres.repo_name.as_ref()) {
|
||||
(Some(o), Some(r)) => (Some(o.clone()), Some(r.clone())),
|
||||
_ => parse_owner_repo(&jobres.repo_url).map(|(o, r)| (Some(o), Some(r))).unwrap_or((None, None)),
|
||||
_ => parse_owner_repo(&jobres.repo_url)
|
||||
.map(|(o, r)| (Some(o), Some(r)))
|
||||
.unwrap_or((None, None)),
|
||||
};
|
||||
|
||||
let Some(owner) = owner else { return Ok(()); };
|
||||
let Some(repo) = repo else { return Ok(()); };
|
||||
let Some(owner) = owner else {
|
||||
return Ok(());
|
||||
};
|
||||
let Some(repo) = repo else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let installation_id = match get_installation_id_for_repo(state, &owner, &repo).await? {
|
||||
Some(id) => id,
|
||||
|
|
@ -1310,14 +1319,25 @@ async fn handle_job_result(state: &AppState, jobres: &common::messages::JobResul
|
|||
};
|
||||
|
||||
let conclusion = if jobres.success { "success" } else { "failure" };
|
||||
let summary = jobres
|
||||
.summary
|
||||
.clone()
|
||||
.unwrap_or_else(|| if jobres.success { "Job succeeded" } else { "Job failed" }.to_string());
|
||||
let summary = jobres.summary.clone().unwrap_or_else(|| {
|
||||
if jobres.success {
|
||||
"Job succeeded"
|
||||
} else {
|
||||
"Job failed"
|
||||
}
|
||||
.to_string()
|
||||
});
|
||||
|
||||
let external_id = jobres.request_id.to_string();
|
||||
if let Some(check_run_id) =
|
||||
find_check_run_id(state, &token, &owner, &repo, &jobres.commit_sha, &external_id).await?
|
||||
if let Some(check_run_id) = find_check_run_id(
|
||||
state,
|
||||
&token,
|
||||
&owner,
|
||||
&repo,
|
||||
&jobres.commit_sha,
|
||||
&external_id,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
let _ = update_check_run(
|
||||
state,
|
||||
|
|
|
|||
|
|
@ -96,7 +96,10 @@ impl LogsClient {
|
|||
}
|
||||
|
||||
pub async fn list_jobs(&self) -> Result<Vec<JobGroup>> {
|
||||
let url = self.base_url.join("jobs").map_err(LogsClientError::InvalidBaseUrl)?;
|
||||
let url = self
|
||||
.base_url
|
||||
.join("jobs")
|
||||
.map_err(LogsClientError::InvalidBaseUrl)?;
|
||||
self.get_json(url).await
|
||||
}
|
||||
|
||||
|
|
@ -105,11 +108,7 @@ impl LogsClient {
|
|||
self.get_json(url).await
|
||||
}
|
||||
|
||||
pub async fn get_logs_by_category(
|
||||
&self,
|
||||
request_id: Uuid,
|
||||
category: &str,
|
||||
) -> Result<String> {
|
||||
pub async fn get_logs_by_category(&self, request_id: Uuid, category: &str) -> Result<String> {
|
||||
let url = self.job_logs_url(request_id, Some(category))?;
|
||||
self.get_text(url).await
|
||||
}
|
||||
|
|
@ -129,8 +128,8 @@ impl LogsClient {
|
|||
}
|
||||
|
||||
async fn get_json<T: DeserializeOwned>(&self, url: Url) -> Result<T> {
|
||||
let resp = self
|
||||
.client
|
||||
let resp =
|
||||
self.client
|
||||
.get(url.clone())
|
||||
.send()
|
||||
.await
|
||||
|
|
@ -158,8 +157,8 @@ impl LogsClient {
|
|||
}
|
||||
|
||||
async fn get_text(&self, url: Url) -> Result<String> {
|
||||
let resp = self
|
||||
.client
|
||||
let resp =
|
||||
self.client
|
||||
.get(url.clone())
|
||||
.send()
|
||||
.await
|
||||
|
|
@ -186,9 +185,9 @@ impl LogsClient {
|
|||
fn job_logs_url(&self, request_id: Uuid, category: Option<&str>) -> Result<Url> {
|
||||
let mut url = self.base_url.clone();
|
||||
{
|
||||
let mut segments = url
|
||||
.path_segments_mut()
|
||||
.map_err(|_| LogsClientError::InvalidBaseUrl(url::ParseError::RelativeUrlWithoutBase))?;
|
||||
let mut segments = url.path_segments_mut().map_err(|_| {
|
||||
LogsClientError::InvalidBaseUrl(url::ParseError::RelativeUrlWithoutBase)
|
||||
})?;
|
||||
segments.clear();
|
||||
segments.extend(&["jobs", &request_id.to_string(), "logs"]);
|
||||
if let Some(cat) = category {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use std::net::IpAddr;
|
||||
use base64::Engine;
|
||||
use hmac::{Hmac, Mac};
|
||||
use http::HeaderMap;
|
||||
use miette::Diagnostic;
|
||||
use serde::de::DeserializeOwned;
|
||||
use sha2::Sha256;
|
||||
use miette::Diagnostic;
|
||||
use std::net::IpAddr;
|
||||
use thiserror::Error;
|
||||
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
|
|
@ -102,13 +102,17 @@ pub enum SignatureError {
|
|||
#[error("missing signature headers for {0:?}")]
|
||||
#[diagnostic(
|
||||
code("webhook.signature.missing"),
|
||||
help("Ensure the webhook sender or proxy includes the expected signature header(s) for one of the enabled signature sources.")
|
||||
help(
|
||||
"Ensure the webhook sender or proxy includes the expected signature header(s) for one of the enabled signature sources."
|
||||
)
|
||||
)]
|
||||
Missing(Vec<SignatureSource>),
|
||||
#[error("invalid signature for {0:?}")]
|
||||
#[diagnostic(
|
||||
code("webhook.signature.invalid"),
|
||||
help("Check that the signing secret matches the sender/proxy configuration and that the request body is unmodified.")
|
||||
help(
|
||||
"Check that the signing secret matches the sender/proxy configuration and that the request body is unmodified."
|
||||
)
|
||||
)]
|
||||
Invalid(Vec<SignatureSource>),
|
||||
}
|
||||
|
|
@ -124,7 +128,9 @@ enum SignatureCheckError {
|
|||
#[error("signature verification failed")]
|
||||
#[diagnostic(
|
||||
code("webhook.signature.verify_failed"),
|
||||
help("Confirm the signing secret and ensure the request body is not modified by intermediaries.")
|
||||
help(
|
||||
"Confirm the signing secret and ensure the request body is not modified by intermediaries."
|
||||
)
|
||||
)]
|
||||
Invalid,
|
||||
}
|
||||
|
|
@ -214,7 +220,10 @@ impl WebhookInfo {
|
|||
delivery: header_string(headers, "X-GitHub-Delivery"),
|
||||
hook_id: header_u64(headers, "X-GitHub-Hook-Id"),
|
||||
installation_target_id: header_u64(headers, "X-GitHub-Hook-Installation-Target-Id"),
|
||||
installation_target_type: header_string(headers, "X-GitHub-Hook-Installation-Target-Type"),
|
||||
installation_target_type: header_string(
|
||||
headers,
|
||||
"X-GitHub-Hook-Installation-Target-Type",
|
||||
),
|
||||
user_agent: header_string(headers, "User-Agent"),
|
||||
};
|
||||
|
||||
|
|
@ -255,7 +264,9 @@ pub enum WebhookError {
|
|||
#[error("signature verification failed")]
|
||||
#[diagnostic(
|
||||
code("webhook.signature.failed"),
|
||||
help("Provide a valid signature header or disable verification only in trusted development environments.")
|
||||
help(
|
||||
"Provide a valid signature header or disable verification only in trusted development environments."
|
||||
)
|
||||
)]
|
||||
Signature(#[from] SignatureError),
|
||||
#[error("failed to parse json payload")]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue