text/x-rust
•
3.73 KB
•
142 lines
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use uuid::Uuid;
use super::common::format_relative_time;
/// Status of a CI job
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum CiJobStatus {
#[default]
Pending,
Running,
Success,
Failed,
}
impl CiJobStatus {
pub fn as_str(&self) -> &'static str {
match self {
Self::Pending => "pending",
Self::Running => "running",
Self::Success => "success",
Self::Failed => "failed",
}
}
}
/// A CI job record
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CiJob {
pub id: Uuid,
pub owner: String,
pub project: String,
pub workflow_name: String,
pub branch: String,
pub commit_hash: String,
pub status: CiJobStatus,
#[serde(with = "time::serde::rfc3339")]
pub created_at: OffsetDateTime,
#[serde(default, with = "time::serde::rfc3339::option")]
pub started_at: Option<OffsetDateTime>,
#[serde(default, with = "time::serde::rfc3339::option")]
pub finished_at: Option<OffsetDateTime>,
pub exit_code: Option<i32>,
}
impl CiJob {
/// Create a new pending job
pub fn new(
owner: String,
project: String,
workflow_name: String,
branch: String,
commit_hash: String,
) -> Self {
Self {
id: Uuid::now_v7(),
owner,
project,
workflow_name,
branch,
commit_hash,
status: CiJobStatus::Pending,
created_at: OffsetDateTime::now_utc(),
started_at: None,
finished_at: None,
exit_code: None,
}
}
/// Get the directory path for this job's data
pub fn job_dir(&self, ci_root: &std::path::Path) -> PathBuf {
ci_root
.join(&self.owner)
.join(&self.project)
.join(self.id.to_string())
}
/// Get the path to the job metadata file
pub fn job_file(&self, ci_root: &std::path::Path) -> PathBuf {
self.job_dir(ci_root).join("job.json")
}
/// Get the path to the log file
pub fn log_file(&self, ci_root: &std::path::Path) -> PathBuf {
self.job_dir(ci_root).join("output.log")
}
/// Format the created_at date for display
pub fn relative_time(&self) -> String {
let now = OffsetDateTime::now_utc();
let diff = now - self.created_at;
format_relative_time(diff.whole_seconds())
}
/// Get a short version of the commit hash
pub fn short_hash(&self) -> &str {
if self.commit_hash.len() > 8 {
&self.commit_hash[..8]
} else {
&self.commit_hash
}
}
/// URI for viewing this job
pub fn uri(&self) -> String {
format!("/~{}/{}/ci/{}", self.owner, self.project, self.id)
}
}
/// Workflow configuration parsed from .rubhub/workflows/*.yaml
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CiWorkflow {
pub name: String,
#[serde(default)]
pub packages: Vec<String>,
pub script: String,
#[serde(default)]
pub branches: Vec<String>,
}
impl CiWorkflow {
/// Check if this workflow should run on the given branch
pub fn should_run_on_branch(&self, branch: &str) -> bool {
// Empty branches list means run on all branches
self.branches.is_empty() || self.branches.iter().any(|b| b == branch)
}
}
/// Request to run a CI job (sent through the channel)
#[derive(Debug, Clone)]
pub struct CiJobRequest {
pub owner: String,
pub project: String,
pub branch: String,
pub commit_hash: String,
pub workflow_name: String,
pub workflow: CiWorkflow,
}