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)?;
|
let base_url = resolve_logs_base_url(logs_base_url)?;
|
||||||
cmd_logs(&base_url, &job_id, category.as_deref()).await?;
|
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 base_url = resolve_logs_base_url(logs_base_url)?;
|
||||||
let repo = resolve_repo_url(repo);
|
let repo = resolve_repo_url(repo);
|
||||||
run_tui(&base_url, repo).await?;
|
run_tui(&base_url, repo).await?;
|
||||||
|
|
@ -170,11 +173,7 @@ fn detect_git_remote() -> Option<String> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let s = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
let s = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||||
if s.is_empty() {
|
if s.is_empty() { None } else { Some(s) }
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(s)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn config_path() -> Result<PathBuf> {
|
fn config_path() -> Result<PathBuf> {
|
||||||
|
|
@ -185,7 +184,9 @@ fn config_path() -> Result<PathBuf> {
|
||||||
} else if let Ok(home) = std::env::var("USERPROFILE") {
|
} else if let Ok(home) = std::env::var("USERPROFILE") {
|
||||||
PathBuf::from(home).join(".config")
|
PathBuf::from(home).join(".config")
|
||||||
} else {
|
} 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"))
|
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 {
|
let Some(node) = doc.get("logs_base_url") else {
|
||||||
return Ok(None);
|
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);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
if value.trim().is_empty() {
|
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<()> {
|
async fn cmd_jobs(base_url: &str, repo: Option<&str>) -> Result<()> {
|
||||||
let client = LogsClient::new(base_url)
|
let client = LogsClient::new(base_url).map_err(|err| miette::Report::msg(err.to_string()))?;
|
||||||
.map_err(|err| miette::Report::msg(err.to_string()))?;
|
|
||||||
let groups = client
|
let groups = client
|
||||||
.list_jobs()
|
.list_jobs()
|
||||||
.await
|
.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<()> {
|
async fn cmd_logs(base_url: &str, job_id: &str, category: Option<&str>) -> Result<()> {
|
||||||
let client = LogsClient::new(base_url)
|
let client = LogsClient::new(base_url).map_err(|err| miette::Report::msg(err.to_string()))?;
|
||||||
.map_err(|err| miette::Report::msg(err.to_string()))?;
|
|
||||||
let request_id = uuid::Uuid::parse_str(job_id).into_diagnostic()?;
|
let request_id = uuid::Uuid::parse_str(job_id).into_diagnostic()?;
|
||||||
let text = if let Some(cat) = category {
|
let text = if let Some(cat) = category {
|
||||||
client
|
client
|
||||||
|
|
@ -324,7 +327,9 @@ fn setup_terminal() -> Result<Terminal<ratatui::backend::CrosstermBackend<Stdout
|
||||||
Terminal::new(backend).into_diagnostic()
|
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()?;
|
terminal::disable_raw_mode().into_diagnostic()?;
|
||||||
execute!(terminal.backend_mut(), LeaveAlternateScreen).into_diagnostic()?;
|
execute!(terminal.backend_mut(), LeaveAlternateScreen).into_diagnostic()?;
|
||||||
terminal.show_cursor().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> {
|
fn filter_groups_by_repo(groups: Vec<JobGroup>, repo: Option<&str>) -> Vec<JobGroup> {
|
||||||
if let Some(repo) = repo {
|
if let Some(repo) = repo {
|
||||||
groups
|
groups.into_iter().filter(|g| g.repo_url == repo).collect()
|
||||||
.into_iter()
|
|
||||||
.filter(|g| g.repo_url == repo)
|
|
||||||
.collect()
|
|
||||||
} else {
|
} else {
|
||||||
groups
|
groups
|
||||||
}
|
}
|
||||||
|
|
@ -434,20 +436,14 @@ impl TuiApp {
|
||||||
.map_err(|err| miette::Report::msg(err.to_string()))?;
|
.map_err(|err| miette::Report::msg(err.to_string()))?;
|
||||||
let mut repos_map: BTreeMap<String, Vec<JobEntry>> = BTreeMap::new();
|
let mut repos_map: BTreeMap<String, Vec<JobEntry>> = BTreeMap::new();
|
||||||
for group in groups {
|
for group in groups {
|
||||||
let entries = group
|
let entries = group.jobs.into_iter().map(|job| JobEntry {
|
||||||
.jobs
|
request_id: job.request_id,
|
||||||
.into_iter()
|
commit_sha: group.commit_sha.clone(),
|
||||||
.map(|job| JobEntry {
|
state: job.state,
|
||||||
request_id: job.request_id,
|
runs_on: job.runs_on,
|
||||||
commit_sha: group.commit_sha.clone(),
|
updated_at: job.updated_at,
|
||||||
state: job.state,
|
});
|
||||||
runs_on: job.runs_on,
|
repos_map.entry(group.repo_url).or_default().extend(entries);
|
||||||
updated_at: job.updated_at,
|
|
||||||
});
|
|
||||||
repos_map
|
|
||||||
.entry(group.repo_url)
|
|
||||||
.or_default()
|
|
||||||
.extend(entries);
|
|
||||||
}
|
}
|
||||||
let mut repos: Vec<String> = repos_map.keys().cloned().collect();
|
let mut repos: Vec<String> = repos_map.keys().cloned().collect();
|
||||||
repos.sort();
|
repos.sort();
|
||||||
|
|
@ -488,20 +484,13 @@ impl TuiApp {
|
||||||
};
|
};
|
||||||
match self.client.list_log_categories(job.request_id).await {
|
match self.client.list_log_categories(job.request_id).await {
|
||||||
Ok(categories) => {
|
Ok(categories) => {
|
||||||
self.logs_categories = categories
|
self.logs_categories = categories.into_iter().map(|c| c.category).collect();
|
||||||
.into_iter()
|
|
||||||
.map(|c| c.category)
|
|
||||||
.collect();
|
|
||||||
if self.logs_categories.is_empty() {
|
if self.logs_categories.is_empty() {
|
||||||
self.logs_text = "No logs available.".to_string();
|
self.logs_text = "No logs available.".to_string();
|
||||||
self.logs_category = None;
|
self.logs_category = None;
|
||||||
self.selected_category = 0;
|
self.selected_category = 0;
|
||||||
} else {
|
} else {
|
||||||
if let Some(idx) = self
|
if let Some(idx) = self.logs_categories.iter().position(|c| c == "default") {
|
||||||
.logs_categories
|
|
||||||
.iter()
|
|
||||||
.position(|c| c == "default")
|
|
||||||
{
|
|
||||||
self.selected_category = idx;
|
self.selected_category = idx;
|
||||||
} else if self.selected_category >= self.logs_categories.len() {
|
} else if self.selected_category >= self.logs_categories.len() {
|
||||||
self.selected_category = 0;
|
self.selected_category = 0;
|
||||||
|
|
@ -624,7 +613,11 @@ impl TuiApp {
|
||||||
let size = frame.area();
|
let size = frame.area();
|
||||||
let layout = Layout::default()
|
let layout = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.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);
|
.split(size);
|
||||||
self.draw_header(frame, layout[0]);
|
self.draw_header(frame, layout[0]);
|
||||||
self.draw_body(frame, layout[1]);
|
self.draw_body(frame, layout[1]);
|
||||||
|
|
@ -796,9 +789,7 @@ fn parse_sgr_params(bytes: &[u8]) -> Vec<u16> {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
let s = String::from_utf8_lossy(bytes);
|
let s = String::from_utf8_lossy(bytes);
|
||||||
s.split(';')
|
s.split(';').filter_map(|p| p.parse::<u16>().ok()).collect()
|
||||||
.filter_map(|p| p.parse::<u16>().ok())
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_sgr(style: &mut Style, params: &[u16]) {
|
fn apply_sgr(style: &mut Style, params: &[u16]) {
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,11 @@ struct Opts {
|
||||||
hookdeck_signing_secret: Option<String>,
|
hookdeck_signing_secret: Option<String>,
|
||||||
|
|
||||||
/// GitHub API base (e.g., https://api.github.com)
|
/// 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_api_base: String,
|
||||||
|
|
||||||
/// GitHub App ID
|
/// GitHub App ID
|
||||||
|
|
@ -186,7 +190,9 @@ async fn main() -> Result<()> {
|
||||||
.hookdeck_signing_secret
|
.hookdeck_signing_secret
|
||||||
.or_else(|| std::env::var("HOOKDECK_SECRET").ok());
|
.or_else(|| std::env::var("HOOKDECK_SECRET").ok());
|
||||||
if webhook_secret.is_none() && hookdeck_signing_secret.is_none() {
|
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) {
|
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);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let v: serde_json::Value = resp.json().await.into_diagnostic()?;
|
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(
|
async fn get_installation_id_for_repo(
|
||||||
|
|
@ -487,15 +495,13 @@ async fn handle_webhook(
|
||||||
Ok(extract) => handle_pull_request(state, extract.payload).await,
|
Ok(extract) => handle_pull_request(state, extract.payload).await,
|
||||||
Err(err) => map_webhook_error(err),
|
Err(err) => map_webhook_error(err),
|
||||||
},
|
},
|
||||||
"ping" => match webhook::verify_signatures(
|
"ping" => {
|
||||||
&headers,
|
match webhook::verify_signatures(&headers, body.as_ref(), &checks, SignaturePolicy::Any)
|
||||||
body.as_ref(),
|
{
|
||||||
&checks,
|
Ok(_) => StatusCode::OK,
|
||||||
SignaturePolicy::Any,
|
Err(err) => map_webhook_error(WebhookError::Signature(err)),
|
||||||
) {
|
}
|
||||||
Ok(_) => StatusCode::OK,
|
}
|
||||||
Err(err) => map_webhook_error(WebhookError::Signature(err)),
|
|
||||||
},
|
|
||||||
_ => StatusCode::NO_CONTENT,
|
_ => StatusCode::NO_CONTENT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1290,16 +1296,19 @@ async fn handle_job_result(state: &AppState, jobres: &common::messages::JobResul
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (owner, repo) = match (
|
let (owner, repo) = match (jobres.repo_owner.as_ref(), jobres.repo_name.as_ref()) {
|
||||||
jobres.repo_owner.as_ref(),
|
|
||||||
jobres.repo_name.as_ref(),
|
|
||||||
) {
|
|
||||||
(Some(o), Some(r)) => (Some(o.clone()), Some(r.clone())),
|
(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(owner) = owner else {
|
||||||
let Some(repo) = repo else { return Ok(()); };
|
return Ok(());
|
||||||
|
};
|
||||||
|
let Some(repo) = repo else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
let installation_id = match get_installation_id_for_repo(state, &owner, &repo).await? {
|
let installation_id = match get_installation_id_for_repo(state, &owner, &repo).await? {
|
||||||
Some(id) => id,
|
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 conclusion = if jobres.success { "success" } else { "failure" };
|
||||||
let summary = jobres
|
let summary = jobres.summary.clone().unwrap_or_else(|| {
|
||||||
.summary
|
if jobres.success {
|
||||||
.clone()
|
"Job succeeded"
|
||||||
.unwrap_or_else(|| if jobres.success { "Job succeeded" } else { "Job failed" }.to_string());
|
} else {
|
||||||
|
"Job failed"
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
});
|
||||||
|
|
||||||
let external_id = jobres.request_id.to_string();
|
let external_id = jobres.request_id.to_string();
|
||||||
if let Some(check_run_id) =
|
if let Some(check_run_id) = find_check_run_id(
|
||||||
find_check_run_id(state, &token, &owner, &repo, &jobres.commit_sha, &external_id).await?
|
state,
|
||||||
|
&token,
|
||||||
|
&owner,
|
||||||
|
&repo,
|
||||||
|
&jobres.commit_sha,
|
||||||
|
&external_id,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
{
|
{
|
||||||
let _ = update_check_run(
|
let _ = update_check_run(
|
||||||
state,
|
state,
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,10 @@ impl LogsClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_jobs(&self) -> Result<Vec<JobGroup>> {
|
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
|
self.get_json(url).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,11 +108,7 @@ impl LogsClient {
|
||||||
self.get_json(url).await
|
self.get_json(url).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_logs_by_category(
|
pub async fn get_logs_by_category(&self, request_id: Uuid, category: &str) -> Result<String> {
|
||||||
&self,
|
|
||||||
request_id: Uuid,
|
|
||||||
category: &str,
|
|
||||||
) -> Result<String> {
|
|
||||||
let url = self.job_logs_url(request_id, Some(category))?;
|
let url = self.job_logs_url(request_id, Some(category))?;
|
||||||
self.get_text(url).await
|
self.get_text(url).await
|
||||||
}
|
}
|
||||||
|
|
@ -129,15 +128,15 @@ impl LogsClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_json<T: DeserializeOwned>(&self, url: Url) -> Result<T> {
|
async fn get_json<T: DeserializeOwned>(&self, url: Url) -> Result<T> {
|
||||||
let resp = self
|
let resp =
|
||||||
.client
|
self.client
|
||||||
.get(url.clone())
|
.get(url.clone())
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| LogsClientError::Request {
|
.map_err(|e| LogsClientError::Request {
|
||||||
url: url.to_string(),
|
url: url.to_string(),
|
||||||
source: e,
|
source: e,
|
||||||
})?;
|
})?;
|
||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
let bytes = resp.bytes().await.map_err(|e| LogsClientError::Request {
|
let bytes = resp.bytes().await.map_err(|e| LogsClientError::Request {
|
||||||
url: url.to_string(),
|
url: url.to_string(),
|
||||||
|
|
@ -158,15 +157,15 @@ impl LogsClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_text(&self, url: Url) -> Result<String> {
|
async fn get_text(&self, url: Url) -> Result<String> {
|
||||||
let resp = self
|
let resp =
|
||||||
.client
|
self.client
|
||||||
.get(url.clone())
|
.get(url.clone())
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| LogsClientError::Request {
|
.map_err(|e| LogsClientError::Request {
|
||||||
url: url.to_string(),
|
url: url.to_string(),
|
||||||
source: e,
|
source: e,
|
||||||
})?;
|
})?;
|
||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
let bytes = resp.bytes().await.map_err(|e| LogsClientError::Request {
|
let bytes = resp.bytes().await.map_err(|e| LogsClientError::Request {
|
||||||
url: url.to_string(),
|
url: url.to_string(),
|
||||||
|
|
@ -186,9 +185,9 @@ impl LogsClient {
|
||||||
fn job_logs_url(&self, request_id: Uuid, category: Option<&str>) -> Result<Url> {
|
fn job_logs_url(&self, request_id: Uuid, category: Option<&str>) -> Result<Url> {
|
||||||
let mut url = self.base_url.clone();
|
let mut url = self.base_url.clone();
|
||||||
{
|
{
|
||||||
let mut segments = url
|
let mut segments = url.path_segments_mut().map_err(|_| {
|
||||||
.path_segments_mut()
|
LogsClientError::InvalidBaseUrl(url::ParseError::RelativeUrlWithoutBase)
|
||||||
.map_err(|_| LogsClientError::InvalidBaseUrl(url::ParseError::RelativeUrlWithoutBase))?;
|
})?;
|
||||||
segments.clear();
|
segments.clear();
|
||||||
segments.extend(&["jobs", &request_id.to_string(), "logs"]);
|
segments.extend(&["jobs", &request_id.to_string(), "logs"]);
|
||||||
if let Some(cat) = category {
|
if let Some(cat) = category {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use std::net::IpAddr;
|
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::{Hmac, Mac};
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
|
use miette::Diagnostic;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
use miette::Diagnostic;
|
use std::net::IpAddr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
type HmacSha256 = Hmac<Sha256>;
|
type HmacSha256 = Hmac<Sha256>;
|
||||||
|
|
@ -102,13 +102,17 @@ pub enum SignatureError {
|
||||||
#[error("missing signature headers for {0:?}")]
|
#[error("missing signature headers for {0:?}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code("webhook.signature.missing"),
|
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>),
|
Missing(Vec<SignatureSource>),
|
||||||
#[error("invalid signature for {0:?}")]
|
#[error("invalid signature for {0:?}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code("webhook.signature.invalid"),
|
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>),
|
Invalid(Vec<SignatureSource>),
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +128,9 @@ enum SignatureCheckError {
|
||||||
#[error("signature verification failed")]
|
#[error("signature verification failed")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code("webhook.signature.verify_failed"),
|
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,
|
Invalid,
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +220,10 @@ impl WebhookInfo {
|
||||||
delivery: header_string(headers, "X-GitHub-Delivery"),
|
delivery: header_string(headers, "X-GitHub-Delivery"),
|
||||||
hook_id: header_u64(headers, "X-GitHub-Hook-Id"),
|
hook_id: header_u64(headers, "X-GitHub-Hook-Id"),
|
||||||
installation_target_id: header_u64(headers, "X-GitHub-Hook-Installation-Target-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"),
|
user_agent: header_string(headers, "User-Agent"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -255,7 +264,9 @@ pub enum WebhookError {
|
||||||
#[error("signature verification failed")]
|
#[error("signature verification failed")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code("webhook.signature.failed"),
|
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),
|
Signature(#[from] SignatureError),
|
||||||
#[error("failed to parse json payload")]
|
#[error("failed to parse json payload")]
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue