mirror of
https://codeberg.org/Toasterson/solstice-ci.git
synced 2026-04-10 21:30:41 +00:00
Add repository owner/name parsing and integrate with commit status updates
This commit introduces: - A utility function to parse repository owner and name from URLs, supporting HTTPS, SSH, and Git formats. - Enhancements to job messages and results with optional `repo_owner` and `repo_name` fields for downstream integrations. - Updated orchestrator and forge-integration workflows to leverage parsed repository details for status updates and accurate routing.
This commit is contained in:
parent
c00ce54112
commit
06ae079b14
4 changed files with 87 additions and 14 deletions
2
TODO.txt
2
TODO.txt
|
|
@ -1,6 +1,6 @@
|
||||||
- Return status to codeberg: Extract repo from Webhook and keep in messages
|
|
||||||
- Make RabbitMQ Messages Print nicely
|
- Make RabbitMQ Messages Print nicely
|
||||||
- move runner logs to debug level so they can be logged in the CI job but don't spam the deployed version
|
- move runner logs to debug level so they can be logged in the CI job but don't spam the deployed version
|
||||||
- Make Orchestrator serve the runner binaries so no external server is needed
|
- Make Orchestrator serve the runner binaries so no external server is needed
|
||||||
- Make orchestrator detect the address it will be reachable by checking the libvirt config or on illumos use it's external IP
|
- Make orchestrator detect the address it will be reachable by checking the libvirt config or on illumos use it's external IP
|
||||||
- Make VM reachable IP of the orchestrator configurable in case the setup on illumos gets more complicated (via config file)
|
- Make VM reachable IP of the orchestrator configurable in case the setup on illumos gets more complicated (via config file)
|
||||||
|
- Make the forge-integration task use fnox secrets
|
||||||
|
|
@ -15,6 +15,12 @@ pub struct JobRequest {
|
||||||
pub source: SourceSystem,
|
pub source: SourceSystem,
|
||||||
/// Repository clone URL (SSH or HTTPS).
|
/// Repository clone URL (SSH or HTTPS).
|
||||||
pub repo_url: String,
|
pub repo_url: String,
|
||||||
|
/// Repository owner (parsed from webhook or URL); optional for backward-compat.
|
||||||
|
#[serde(default)]
|
||||||
|
pub repo_owner: Option<String>,
|
||||||
|
/// Repository name (parsed from webhook or URL); optional for backward-compat.
|
||||||
|
#[serde(default)]
|
||||||
|
pub repo_name: Option<String>,
|
||||||
/// Commit SHA to check out.
|
/// Commit SHA to check out.
|
||||||
pub commit_sha: String,
|
pub commit_sha: String,
|
||||||
/// Optional path to the workflow file within the repo (KDL).
|
/// Optional path to the workflow file within the repo (KDL).
|
||||||
|
|
@ -50,6 +56,8 @@ impl JobRequest {
|
||||||
request_id: Uuid::new_v4(),
|
request_id: Uuid::new_v4(),
|
||||||
source,
|
source,
|
||||||
repo_url: repo_url.into(),
|
repo_url: repo_url.into(),
|
||||||
|
repo_owner: None,
|
||||||
|
repo_name: None,
|
||||||
commit_sha: commit_sha.into(),
|
commit_sha: commit_sha.into(),
|
||||||
workflow_path: None,
|
workflow_path: None,
|
||||||
workflow_job_id: None,
|
workflow_job_id: None,
|
||||||
|
|
@ -69,6 +77,12 @@ pub struct JobResult {
|
||||||
pub request_id: Uuid,
|
pub request_id: Uuid,
|
||||||
/// Repository and commit info (for convenience in consumers)
|
/// Repository and commit info (for convenience in consumers)
|
||||||
pub repo_url: String,
|
pub repo_url: String,
|
||||||
|
/// Repository owner (when known). Optional for backward-compat.
|
||||||
|
#[serde(default)]
|
||||||
|
pub repo_owner: Option<String>,
|
||||||
|
/// Repository name (when known). Optional for backward-compat.
|
||||||
|
#[serde(default)]
|
||||||
|
pub repo_name: Option<String>,
|
||||||
pub commit_sha: String,
|
pub commit_sha: String,
|
||||||
/// Outcome info
|
/// Outcome info
|
||||||
pub success: bool,
|
pub success: bool,
|
||||||
|
|
@ -96,6 +110,8 @@ impl JobResult {
|
||||||
schema_version: default_jobresult_schema(),
|
schema_version: default_jobresult_schema(),
|
||||||
request_id,
|
request_id,
|
||||||
repo_url,
|
repo_url,
|
||||||
|
repo_owner: None,
|
||||||
|
repo_name: None,
|
||||||
commit_sha,
|
commit_sha,
|
||||||
success,
|
success,
|
||||||
exit_code,
|
exit_code,
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,20 @@ async fn post_commit_status(
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Extract owner/repo from repo_url (supports https://.../owner/repo.git and ssh://git@host/owner/repo.git)
|
// Extract owner/repo from repo_url (supports https://.../owner/repo.git and ssh://git@host/owner/repo.git)
|
||||||
let (owner, repo) = parse_owner_repo(repo_url).ok_or_else(|| miette::miette!("cannot parse owner/repo from repo_url: {repo_url}"))?;
|
let (owner, repo) = parse_owner_repo(repo_url).ok_or_else(|| miette::miette!("cannot parse owner/repo from repo_url: {repo_url}"))?;
|
||||||
|
post_commit_status_owner(base, token, &owner, &repo, sha, context, state, target_url, description).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_commit_status_owner(
|
||||||
|
base: &str,
|
||||||
|
token: &str,
|
||||||
|
owner: &str,
|
||||||
|
repo: &str,
|
||||||
|
sha: &str,
|
||||||
|
context: &str,
|
||||||
|
state: &str,
|
||||||
|
target_url: Option<&str>,
|
||||||
|
description: Option<&str>,
|
||||||
|
) -> Result<()> {
|
||||||
let api = format!("{}/repos/{}/{}/statuses/{}", base.trim_end_matches('/'), owner, repo, sha);
|
let api = format!("{}/repos/{}/{}/statuses/{}", base.trim_end_matches('/'), owner, repo, sha);
|
||||||
let mut body = serde_json::json!({
|
let mut body = serde_json::json!({
|
||||||
"state": state,
|
"state": state,
|
||||||
|
|
@ -377,6 +391,20 @@ async fn handle_job_result(state: &AppState, jobres: &common::messages::JobResul
|
||||||
let state_str = if jobres.success { "success" } else { "failure" };
|
let state_str = if jobres.success { "success" } else { "failure" };
|
||||||
if let (Some(base), Some(token)) = (state.forgejo_base.as_ref(), state.forgejo_token.as_ref()) {
|
if let (Some(base), Some(token)) = (state.forgejo_base.as_ref(), state.forgejo_token.as_ref()) {
|
||||||
let desc = if jobres.success { Some("Job succeeded") } else { Some("Job failed") };
|
let desc = if jobres.success { Some("Job succeeded") } else { Some("Job failed") };
|
||||||
|
// Prefer explicit owner/repo from JobResult when available
|
||||||
|
if let (Some(owner), Some(repo)) = (jobres.repo_owner.as_ref(), jobres.repo_name.as_ref()) {
|
||||||
|
let _ = post_commit_status_owner(
|
||||||
|
base,
|
||||||
|
token,
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
&jobres.commit_sha,
|
||||||
|
&state.forge_context,
|
||||||
|
state_str,
|
||||||
|
target_url.as_deref(),
|
||||||
|
desc,
|
||||||
|
).await;
|
||||||
|
} else {
|
||||||
let _ = post_commit_status(
|
let _ = post_commit_status(
|
||||||
base,
|
base,
|
||||||
token,
|
token,
|
||||||
|
|
@ -386,8 +414,8 @@ async fn handle_job_result(state: &AppState, jobres: &common::messages::JobResul
|
||||||
state_str,
|
state_str,
|
||||||
target_url.as_deref(),
|
target_url.as_deref(),
|
||||||
desc,
|
desc,
|
||||||
)
|
).await;
|
||||||
.await;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -679,6 +707,11 @@ async fn enqueue_job(state: &Arc<AppState>, repo_url: String, commit_sha: String
|
||||||
miette::bail!("missing repo_url in webhook payload");
|
miette::bail!("missing repo_url in webhook payload");
|
||||||
}
|
}
|
||||||
let mut jr = common::JobRequest::new(common::SourceSystem::Forgejo, repo_url, commit_sha);
|
let mut jr = common::JobRequest::new(common::SourceSystem::Forgejo, repo_url, commit_sha);
|
||||||
|
// Try to populate repo_owner/repo_name from URL for accurate status routing
|
||||||
|
if let Some((owner, name)) = parse_owner_repo(&jr.repo_url) {
|
||||||
|
jr.repo_owner = Some(owner);
|
||||||
|
jr.repo_name = Some(name);
|
||||||
|
}
|
||||||
// Infer runs_on from repo map, labels, or default
|
// Infer runs_on from repo map, labels, or default
|
||||||
jr.runs_on = infer_runs_on(state, &jr.repo_url, labels.as_ref().map(|v| v.as_slice()));
|
jr.runs_on = infer_runs_on(state, &jr.repo_url, labels.as_ref().map(|v| v.as_slice()));
|
||||||
common::publish_job(&state.mq_cfg, &jr).await?;
|
common::publish_job(&state.mq_cfg, &jr).await?;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,25 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use crate::persist::Persist;
|
use crate::persist::Persist;
|
||||||
|
|
||||||
|
fn parse_owner_repo(repo_url: &str) -> Option<(String, String)> {
|
||||||
|
let url = repo_url.trim_end_matches(".git");
|
||||||
|
if let Some(rest) = url.strip_prefix("https://").or_else(|| url.strip_prefix("http://")) {
|
||||||
|
let parts: Vec<&str> = rest.split('/').collect();
|
||||||
|
if parts.len() >= 3 { return Some((parts[1].to_string(), parts[2].to_string())); }
|
||||||
|
} else if let Some(rest) = url.strip_prefix("ssh://") {
|
||||||
|
// ssh://git@host/owner/repo
|
||||||
|
let after_host = rest.splitn(2, '/').nth(1)?;
|
||||||
|
let parts: Vec<&str> = after_host.split('/').collect();
|
||||||
|
if parts.len() >= 2 { return Some((parts[0].to_string(), parts[1].to_string())); }
|
||||||
|
} else if let Some(idx) = url.find(':') {
|
||||||
|
// git@host:owner/repo
|
||||||
|
let after = &url[idx+1..];
|
||||||
|
let parts: Vec<&str> = after.split('/').collect();
|
||||||
|
if parts.len() >= 2 { return Some((parts[0].to_string(), parts[1].to_string())); }
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RunnerSvc {
|
pub struct RunnerSvc {
|
||||||
mq_cfg: MqConfig,
|
mq_cfg: MqConfig,
|
||||||
persist: Arc<Persist>,
|
persist: Arc<Persist>,
|
||||||
|
|
@ -95,7 +114,7 @@ impl Runner for RunnerSvc {
|
||||||
if let (Some(id), Some(repo), Some(sha)) =
|
if let (Some(id), Some(repo), Some(sha)) =
|
||||||
(req_id.as_ref(), repo_url.as_ref(), commit_sha.as_ref())
|
(req_id.as_ref(), repo_url.as_ref(), commit_sha.as_ref())
|
||||||
{
|
{
|
||||||
let result = common::messages::JobResult::new(
|
let mut result = common::messages::JobResult::new(
|
||||||
id.clone(),
|
id.clone(),
|
||||||
repo.clone(),
|
repo.clone(),
|
||||||
sha.clone(),
|
sha.clone(),
|
||||||
|
|
@ -103,6 +122,11 @@ impl Runner for RunnerSvc {
|
||||||
exit_code,
|
exit_code,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
// Try to parse owner/repo for downstream integrations to avoid reparsing URLs
|
||||||
|
if let Some((owner, name)) = parse_owner_repo(&repo) {
|
||||||
|
result.repo_owner = Some(owner);
|
||||||
|
result.repo_name = Some(name);
|
||||||
|
}
|
||||||
if let Err(e) = publish_job_result(&self.mq_cfg, &result).await {
|
if let Err(e) = publish_job_result(&self.mq_cfg, &result).await {
|
||||||
error!(error = %e, request_id = %id, "failed to publish JobResult");
|
error!(error = %e, request_id = %id, "failed to publish JobResult");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue