treewide: format with nightly rustfmt; auto-fix Clippy lints
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: If4fd0511087dbaa65afc56a34d7c2f166a6a6964
This commit is contained in:
parent
fe45fff3f3
commit
3a03cf7b3e
26 changed files with 222 additions and 161 deletions
|
|
@ -36,7 +36,12 @@ fn expand_path(path: &str) -> String {
|
||||||
if let Some(end) = result[start..].find('}') {
|
if let Some(end) = result[start..].find('}') {
|
||||||
let var_name = &result[start + 2..start + end];
|
let var_name = &result[start + 2..start + end];
|
||||||
let replacement = std::env::var(var_name).unwrap_or_default();
|
let replacement = std::env::var(var_name).unwrap_or_default();
|
||||||
result = format!("{}{}{}", &result[..start], replacement, &result[start + end + 1..]);
|
result = format!(
|
||||||
|
"{}{}{}",
|
||||||
|
&result[..start],
|
||||||
|
replacement,
|
||||||
|
&result[start + end + 1..]
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -112,12 +117,14 @@ pub async fn run(pool: &PgPool, config: &DeclarativeConfig) -> Result<()> {
|
||||||
|
|
||||||
for decl_jobset in &decl_project.jobsets {
|
for decl_jobset in &decl_project.jobsets {
|
||||||
// Parse state string to JobsetState enum
|
// Parse state string to JobsetState enum
|
||||||
let state = decl_jobset.state.as_ref().map(|s| match s.as_str() {
|
let state = decl_jobset.state.as_ref().map(|s| {
|
||||||
|
match s.as_str() {
|
||||||
"disabled" => JobsetState::Disabled,
|
"disabled" => JobsetState::Disabled,
|
||||||
"enabled" => JobsetState::Enabled,
|
"enabled" => JobsetState::Enabled,
|
||||||
"one_shot" => JobsetState::OneShot,
|
"one_shot" => JobsetState::OneShot,
|
||||||
"one_at_a_time" => JobsetState::OneAtATime,
|
"one_at_a_time" => JobsetState::OneAtATime,
|
||||||
_ => JobsetState::Enabled, // Default to enabled for unknown values
|
_ => JobsetState::Enabled, // Default to enabled for unknown values
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let jobset = repo::jobsets::upsert(pool, CreateJobset {
|
let jobset = repo::jobsets::upsert(pool, CreateJobset {
|
||||||
|
|
@ -141,7 +148,11 @@ pub async fn run(pool: &PgPool, config: &DeclarativeConfig) -> Result<()> {
|
||||||
|
|
||||||
// Sync jobset inputs
|
// Sync jobset inputs
|
||||||
if !decl_jobset.inputs.is_empty() {
|
if !decl_jobset.inputs.is_empty() {
|
||||||
repo::jobset_inputs::sync_for_jobset(pool, jobset.id, &decl_jobset.inputs)
|
repo::jobset_inputs::sync_for_jobset(
|
||||||
|
pool,
|
||||||
|
jobset.id,
|
||||||
|
&decl_jobset.inputs,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
project = %project.name,
|
project = %project.name,
|
||||||
|
|
@ -192,9 +203,12 @@ pub async fn run(pool: &PgPool, config: &DeclarativeConfig) -> Result<()> {
|
||||||
|
|
||||||
// Sync channels
|
// Sync channels
|
||||||
if !decl_project.channels.is_empty() {
|
if !decl_project.channels.is_empty() {
|
||||||
repo::channels::sync_for_project(pool, project.id, &decl_project.channels, |name| {
|
repo::channels::sync_for_project(
|
||||||
jobset_map.get(name).copied()
|
pool,
|
||||||
})
|
project.id,
|
||||||
|
&decl_project.channels,
|
||||||
|
|name| jobset_map.get(name).copied(),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
project = %project.name,
|
project = %project.name,
|
||||||
|
|
@ -202,7 +216,6 @@ pub async fn run(pool: &PgPool, config: &DeclarativeConfig) -> Result<()> {
|
||||||
"Synced declarative channels"
|
"Synced declarative channels"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upsert API keys
|
// Upsert API keys
|
||||||
|
|
@ -332,7 +345,9 @@ pub async fn run(pool: &PgPool, config: &DeclarativeConfig) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get project by name (already exists from earlier upsert)
|
// Get project by name (already exists from earlier upsert)
|
||||||
if let Ok(project) = repo::projects::get_by_name(pool, &decl_project.name).await {
|
if let Ok(project) =
|
||||||
|
repo::projects::get_by_name(pool, &decl_project.name).await
|
||||||
|
{
|
||||||
repo::project_members::sync_for_project(
|
repo::project_members::sync_for_project(
|
||||||
pool,
|
pool,
|
||||||
project.id,
|
project.id,
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,8 @@ pub struct DeclarativeProject {
|
||||||
/// Declarative notification configuration.
|
/// Declarative notification configuration.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct DeclarativeNotification {
|
pub struct DeclarativeNotification {
|
||||||
/// Notification type: github_status, email, gitlab_status, gitea_status, run_command
|
/// Notification type: github_status, email, gitlab_status, gitea_status,
|
||||||
|
/// run_command
|
||||||
pub notification_type: String,
|
pub notification_type: String,
|
||||||
/// Type-specific configuration (JSON object)
|
/// Type-specific configuration (JSON object)
|
||||||
pub config: serde_json::Value,
|
pub config: serde_json::Value,
|
||||||
|
|
|
||||||
|
|
@ -95,8 +95,7 @@ pub async fn probe_flake(
|
||||||
base_ref
|
base_ref
|
||||||
};
|
};
|
||||||
|
|
||||||
let output =
|
let output = tokio::time::timeout(std::time::Duration::from_mins(1), async {
|
||||||
tokio::time::timeout(std::time::Duration::from_mins(1), async {
|
|
||||||
tokio::process::Command::new("nix")
|
tokio::process::Command::new("nix")
|
||||||
.args([
|
.args([
|
||||||
"--extra-experimental-features",
|
"--extra-experimental-features",
|
||||||
|
|
@ -111,9 +110,7 @@ pub async fn probe_flake(
|
||||||
.await
|
.await
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|_| {
|
.map_err(|_| CiError::Timeout("Flake probe timed out after 60s".to_string()))?
|
||||||
CiError::Timeout("Flake probe timed out after 60s".to_string())
|
|
||||||
})?
|
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
CiError::NixEval(format!("Failed to run nix flake show: {e}"))
|
CiError::NixEval(format!("Failed to run nix flake show: {e}"))
|
||||||
})?;
|
})?;
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@ async fn set_github_status(
|
||||||
build: &Build,
|
build: &Build,
|
||||||
) {
|
) {
|
||||||
// Parse owner/repo from URL
|
// Parse owner/repo from URL
|
||||||
let (owner, repo) = if let Some(v) = parse_github_repo(repo_url) { v } else {
|
let (owner, repo) = if let Some(v) = parse_github_repo(repo_url) {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
warn!("Cannot parse GitHub owner/repo from {repo_url}");
|
warn!("Cannot parse GitHub owner/repo from {repo_url}");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -142,7 +144,9 @@ async fn set_gitea_status(
|
||||||
build: &Build,
|
build: &Build,
|
||||||
) {
|
) {
|
||||||
// Parse owner/repo from URL (try to extract from the gitea URL)
|
// Parse owner/repo from URL (try to extract from the gitea URL)
|
||||||
let (owner, repo) = if let Some(v) = parse_gitea_repo(repo_url, base_url) { v } else {
|
let (owner, repo) = if let Some(v) = parse_gitea_repo(repo_url, base_url) {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
warn!("Cannot parse Gitea owner/repo from {repo_url}");
|
warn!("Cannot parse Gitea owner/repo from {repo_url}");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -191,7 +195,9 @@ async fn set_gitlab_status(
|
||||||
build: &Build,
|
build: &Build,
|
||||||
) {
|
) {
|
||||||
// Parse project path from URL
|
// Parse project path from URL
|
||||||
let project_path = if let Some(p) = parse_gitlab_project(repo_url, base_url) { p } else {
|
let project_path = if let Some(p) = parse_gitlab_project(repo_url, base_url) {
|
||||||
|
p
|
||||||
|
} else {
|
||||||
warn!("Cannot parse GitLab project from {repo_url}");
|
warn!("Cannot parse GitLab project from {repo_url}");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,8 @@ pub async fn upsert(
|
||||||
) -> Result<Channel> {
|
) -> Result<Channel> {
|
||||||
sqlx::query_as::<_, Channel>(
|
sqlx::query_as::<_, Channel>(
|
||||||
"INSERT INTO channels (project_id, name, jobset_id) VALUES ($1, $2, $3) \
|
"INSERT INTO channels (project_id, name, jobset_id) VALUES ($1, $2, $3) \
|
||||||
ON CONFLICT (project_id, name) DO UPDATE SET jobset_id = EXCLUDED.jobset_id \
|
ON CONFLICT (project_id, name) DO UPDATE SET jobset_id = \
|
||||||
RETURNING *",
|
EXCLUDED.jobset_id RETURNING *",
|
||||||
)
|
)
|
||||||
.bind(project_id)
|
.bind(project_id)
|
||||||
.bind(name)
|
.bind(name)
|
||||||
|
|
@ -119,7 +119,9 @@ pub async fn sync_for_project(
|
||||||
let names: Vec<&str> = channels.iter().map(|c| c.name.as_str()).collect();
|
let names: Vec<&str> = channels.iter().map(|c| c.name.as_str()).collect();
|
||||||
|
|
||||||
// Delete channels not in declarative config
|
// Delete channels not in declarative config
|
||||||
sqlx::query("DELETE FROM channels WHERE project_id = $1 AND name != ALL($2::text[])")
|
sqlx::query(
|
||||||
|
"DELETE FROM channels WHERE project_id = $1 AND name != ALL($2::text[])",
|
||||||
|
)
|
||||||
.bind(project_id)
|
.bind(project_id)
|
||||||
.bind(&names)
|
.bind(&names)
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,9 @@ pub async fn upsert(
|
||||||
) -> Result<JobsetInput> {
|
) -> Result<JobsetInput> {
|
||||||
sqlx::query_as::<_, JobsetInput>(
|
sqlx::query_as::<_, JobsetInput>(
|
||||||
"INSERT INTO jobset_inputs (jobset_id, name, input_type, value, revision) \
|
"INSERT INTO jobset_inputs (jobset_id, name, input_type, value, revision) \
|
||||||
VALUES ($1, $2, $3, $4, $5) \
|
VALUES ($1, $2, $3, $4, $5) ON CONFLICT (jobset_id, name) DO UPDATE SET \
|
||||||
ON CONFLICT (jobset_id, name) DO UPDATE SET \
|
input_type = EXCLUDED.input_type, value = EXCLUDED.value, revision = \
|
||||||
input_type = EXCLUDED.input_type, \
|
EXCLUDED.revision RETURNING *",
|
||||||
value = EXCLUDED.value, \
|
|
||||||
revision = EXCLUDED.revision \
|
|
||||||
RETURNING *",
|
|
||||||
)
|
)
|
||||||
.bind(jobset_id)
|
.bind(jobset_id)
|
||||||
.bind(name)
|
.bind(name)
|
||||||
|
|
@ -102,7 +99,8 @@ pub async fn sync_for_jobset(
|
||||||
|
|
||||||
// Delete inputs not in declarative config
|
// Delete inputs not in declarative config
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
"DELETE FROM jobset_inputs WHERE jobset_id = $1 AND name != ALL($2::text[])",
|
"DELETE FROM jobset_inputs WHERE jobset_id = $1 AND name != \
|
||||||
|
ALL($2::text[])",
|
||||||
)
|
)
|
||||||
.bind(jobset_id)
|
.bind(jobset_id)
|
||||||
.bind(&names)
|
.bind(&names)
|
||||||
|
|
|
||||||
|
|
@ -231,8 +231,8 @@ pub async fn has_running_builds(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List jobsets that are due for evaluation based on their `check_interval`.
|
/// List jobsets that are due for evaluation based on their `check_interval`.
|
||||||
/// Returns jobsets where `last_checked_at` is NULL or older than `check_interval`
|
/// Returns jobsets where `last_checked_at` is NULL or older than
|
||||||
/// seconds.
|
/// `check_interval` seconds.
|
||||||
pub async fn list_due_for_eval(
|
pub async fn list_due_for_eval(
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
limit: i64,
|
limit: i64,
|
||||||
|
|
|
||||||
|
|
@ -70,9 +70,9 @@ pub async fn upsert(
|
||||||
) -> Result<NotificationConfig> {
|
) -> Result<NotificationConfig> {
|
||||||
sqlx::query_as::<_, NotificationConfig>(
|
sqlx::query_as::<_, NotificationConfig>(
|
||||||
"INSERT INTO notification_configs (project_id, notification_type, config, \
|
"INSERT INTO notification_configs (project_id, notification_type, config, \
|
||||||
enabled) VALUES ($1, $2, $3, $4) ON CONFLICT (project_id, notification_type) \
|
enabled) VALUES ($1, $2, $3, $4) ON CONFLICT (project_id, \
|
||||||
DO UPDATE SET config = EXCLUDED.config, enabled = EXCLUDED.enabled \
|
notification_type) DO UPDATE SET config = EXCLUDED.config, enabled = \
|
||||||
RETURNING *",
|
EXCLUDED.enabled RETURNING *",
|
||||||
)
|
)
|
||||||
.bind(project_id)
|
.bind(project_id)
|
||||||
.bind(notification_type)
|
.bind(notification_type)
|
||||||
|
|
|
||||||
|
|
@ -141,9 +141,7 @@ pub async fn delete(pool: &PgPool, id: Uuid) -> Result<()> {
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
.await?;
|
.await?;
|
||||||
if result.rows_affected() == 0 {
|
if result.rows_affected() == 0 {
|
||||||
return Err(CiError::NotFound(format!(
|
return Err(CiError::NotFound(format!("Project member {id} not found")));
|
||||||
"Project member {id} not found"
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -199,8 +197,8 @@ pub async fn upsert(
|
||||||
.map_err(|e| CiError::Validation(e.to_string()))?;
|
.map_err(|e| CiError::Validation(e.to_string()))?;
|
||||||
|
|
||||||
sqlx::query_as::<_, ProjectMember>(
|
sqlx::query_as::<_, ProjectMember>(
|
||||||
"INSERT INTO project_members (project_id, user_id, role) VALUES ($1, $2, $3) \
|
"INSERT INTO project_members (project_id, user_id, role) VALUES ($1, $2, \
|
||||||
ON CONFLICT (project_id, user_id) DO UPDATE SET role = EXCLUDED.role \
|
$3) ON CONFLICT (project_id, user_id) DO UPDATE SET role = EXCLUDED.role \
|
||||||
RETURNING *",
|
RETURNING *",
|
||||||
)
|
)
|
||||||
.bind(project_id)
|
.bind(project_id)
|
||||||
|
|
|
||||||
|
|
@ -152,9 +152,9 @@ pub async fn upsert(
|
||||||
sqlx::query_as::<_, RemoteBuilder>(
|
sqlx::query_as::<_, RemoteBuilder>(
|
||||||
"INSERT INTO remote_builders (name, ssh_uri, systems, max_jobs, \
|
"INSERT INTO remote_builders (name, ssh_uri, systems, max_jobs, \
|
||||||
speed_factor, supported_features, mandatory_features, enabled, \
|
speed_factor, supported_features, mandatory_features, enabled, \
|
||||||
public_host_key, ssh_key_file) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, \
|
public_host_key, ssh_key_file) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, \
|
||||||
$10) ON CONFLICT (name) DO UPDATE SET ssh_uri = EXCLUDED.ssh_uri, systems = \
|
$9, $10) ON CONFLICT (name) DO UPDATE SET ssh_uri = EXCLUDED.ssh_uri, \
|
||||||
EXCLUDED.systems, max_jobs = EXCLUDED.max_jobs, speed_factor = \
|
systems = EXCLUDED.systems, max_jobs = EXCLUDED.max_jobs, speed_factor = \
|
||||||
EXCLUDED.speed_factor, supported_features = EXCLUDED.supported_features, \
|
EXCLUDED.speed_factor, supported_features = EXCLUDED.supported_features, \
|
||||||
mandatory_features = EXCLUDED.mandatory_features, enabled = \
|
mandatory_features = EXCLUDED.mandatory_features, enabled = \
|
||||||
EXCLUDED.enabled, public_host_key = COALESCE(EXCLUDED.public_host_key, \
|
EXCLUDED.enabled, public_host_key = COALESCE(EXCLUDED.public_host_key, \
|
||||||
|
|
|
||||||
|
|
@ -364,7 +364,8 @@ async fn search_evaluations(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get count - simple count (full filter support would require building query differently)
|
// Get count - simple count (full filter support would require building query
|
||||||
|
// differently)
|
||||||
let (total,): (i64,) = sqlx::query_as("SELECT COUNT(*) FROM evaluations")
|
let (total,): (i64,) = sqlx::query_as("SELECT COUNT(*) FROM evaluations")
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
||||||
|
|
@ -94,10 +94,10 @@ pub async fn upsert(
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
) -> Result<WebhookConfig> {
|
) -> Result<WebhookConfig> {
|
||||||
sqlx::query_as::<_, WebhookConfig>(
|
sqlx::query_as::<_, WebhookConfig>(
|
||||||
"INSERT INTO webhook_configs (project_id, forge_type, secret_hash, enabled) \
|
"INSERT INTO webhook_configs (project_id, forge_type, secret_hash, \
|
||||||
VALUES ($1, $2, $3, $4) ON CONFLICT (project_id, forge_type) DO UPDATE SET \
|
enabled) VALUES ($1, $2, $3, $4) ON CONFLICT (project_id, forge_type) DO \
|
||||||
secret_hash = COALESCE(EXCLUDED.secret_hash, webhook_configs.secret_hash), \
|
UPDATE SET secret_hash = COALESCE(EXCLUDED.secret_hash, \
|
||||||
enabled = EXCLUDED.enabled RETURNING *",
|
webhook_configs.secret_hash), enabled = EXCLUDED.enabled RETURNING *",
|
||||||
)
|
)
|
||||||
.bind(project_id)
|
.bind(project_id)
|
||||||
.bind(forge_type)
|
.bind(forge_type)
|
||||||
|
|
@ -117,7 +117,8 @@ pub async fn sync_for_project(
|
||||||
resolve_secret: impl Fn(&DeclarativeWebhook) -> Option<String>,
|
resolve_secret: impl Fn(&DeclarativeWebhook) -> Option<String>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Get forge types from declarative config
|
// Get forge types from declarative config
|
||||||
let types: Vec<&str> = webhooks.iter().map(|w| w.forge_type.as_str()).collect();
|
let types: Vec<&str> =
|
||||||
|
webhooks.iter().map(|w| w.forge_type.as_str()).collect();
|
||||||
|
|
||||||
// Delete webhook configs not in declarative config
|
// Delete webhook configs not in declarative config
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,8 @@ fn validate_repository_url(url: &str) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
if !VALID_REPO_PREFIXES.iter().any(|p| url.starts_with(p)) {
|
if !VALID_REPO_PREFIXES.iter().any(|p| url.starts_with(p)) {
|
||||||
return Err(
|
return Err(
|
||||||
"repository_url must start with https://, http://, git://, ssh://, or file://"
|
"repository_url must start with https://, http://, git://, ssh://, or \
|
||||||
|
file://"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -146,7 +147,19 @@ fn validate_forge_type(forge_type: &str) -> Result<(), String> {
|
||||||
|
|
||||||
// --- Implementations ---
|
// --- Implementations ---
|
||||||
|
|
||||||
use crate::models::{CreateProject, UpdateProject, CreateJobset, UpdateJobset, CreateEvaluation, CreateBuild, CreateChannel, UpdateChannel, CreateRemoteBuilder, UpdateRemoteBuilder, CreateWebhookConfig};
|
use crate::models::{
|
||||||
|
CreateBuild,
|
||||||
|
CreateChannel,
|
||||||
|
CreateEvaluation,
|
||||||
|
CreateJobset,
|
||||||
|
CreateProject,
|
||||||
|
CreateRemoteBuilder,
|
||||||
|
CreateWebhookConfig,
|
||||||
|
UpdateChannel,
|
||||||
|
UpdateJobset,
|
||||||
|
UpdateProject,
|
||||||
|
UpdateRemoteBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
impl Validate for CreateProject {
|
impl Validate for CreateProject {
|
||||||
fn validate(&self) -> Result<(), String> {
|
fn validate(&self) -> Result<(), String> {
|
||||||
|
|
|
||||||
|
|
@ -476,8 +476,10 @@ async fn run_build(
|
||||||
push_to_cache(&build_result.output_paths, store_uri).await;
|
push_to_cache(&build_result.output_paths, store_uri).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let primary_output =
|
let primary_output = build_result
|
||||||
build_result.output_paths.first().map(std::string::String::as_str);
|
.output_paths
|
||||||
|
.first()
|
||||||
|
.map(std::string::String::as_str);
|
||||||
|
|
||||||
repo::builds::complete(
|
repo::builds::complete(
|
||||||
pool,
|
pool,
|
||||||
|
|
|
||||||
|
|
@ -76,9 +76,7 @@ pub async fn require_api_key(
|
||||||
&& let Some(session) = state.sessions.get(&session_id)
|
&& let Some(session) = state.sessions.get(&session_id)
|
||||||
{
|
{
|
||||||
// Check session expiry (24 hours)
|
// Check session expiry (24 hours)
|
||||||
if session.created_at.elapsed()
|
if session.created_at.elapsed() < std::time::Duration::from_hours(24) {
|
||||||
< std::time::Duration::from_hours(24)
|
|
||||||
{
|
|
||||||
// Insert both user and session data
|
// Insert both user and session data
|
||||||
if let Some(ref user) = session.user {
|
if let Some(ref user) = session.user {
|
||||||
request.extensions_mut().insert(user.clone());
|
request.extensions_mut().insert(user.clone());
|
||||||
|
|
@ -98,9 +96,7 @@ pub async fn require_api_key(
|
||||||
&& let Some(session) = state.sessions.get(&session_id)
|
&& let Some(session) = state.sessions.get(&session_id)
|
||||||
{
|
{
|
||||||
// Check session expiry (24 hours)
|
// Check session expiry (24 hours)
|
||||||
if session.created_at.elapsed()
|
if session.created_at.elapsed() < std::time::Duration::from_hours(24) {
|
||||||
< std::time::Duration::from_hours(24)
|
|
||||||
{
|
|
||||||
if let Some(ref api_key) = session.api_key {
|
if let Some(ref api_key) = session.api_key {
|
||||||
request.extensions_mut().insert(api_key.clone());
|
request.extensions_mut().insert(api_key.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -222,9 +218,7 @@ pub async fn extract_session(
|
||||||
&& let Some(session) = state.sessions.get(&session_id)
|
&& let Some(session) = state.sessions.get(&session_id)
|
||||||
{
|
{
|
||||||
// Check session expiry
|
// Check session expiry
|
||||||
if session.created_at.elapsed()
|
if session.created_at.elapsed() < std::time::Duration::from_hours(24) {
|
||||||
< std::time::Duration::from_hours(24)
|
|
||||||
{
|
|
||||||
if let Some(ref user) = session.user {
|
if let Some(ref user) = session.user {
|
||||||
request.extensions_mut().insert(user.clone());
|
request.extensions_mut().insert(user.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -242,9 +236,7 @@ pub async fn extract_session(
|
||||||
&& let Some(session) = state.sessions.get(&session_id)
|
&& let Some(session) = state.sessions.get(&session_id)
|
||||||
{
|
{
|
||||||
// Check session expiry
|
// Check session expiry
|
||||||
if session.created_at.elapsed()
|
if session.created_at.elapsed() < std::time::Duration::from_hours(24) {
|
||||||
< std::time::Duration::from_hours(24)
|
|
||||||
{
|
|
||||||
if let Some(ref api_key) = session.api_key {
|
if let Some(ref api_key) = session.api_key {
|
||||||
request.extensions_mut().insert(api_key.clone());
|
request.extensions_mut().insert(api_key.clone());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,8 +90,8 @@ impl IntoResponse for ApiError {
|
||||||
StatusCode::INSUFFICIENT_STORAGE,
|
StatusCode::INSUFFICIENT_STORAGE,
|
||||||
"DISK_FULL",
|
"DISK_FULL",
|
||||||
format!(
|
format!(
|
||||||
"Database error: {e}\n\nDISK SPACE ISSUE:\nThe server is running \
|
"Database error: {e}\n\nDISK SPACE ISSUE:\nThe server is \
|
||||||
low on disk space."
|
running low on disk space."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -130,8 +130,8 @@ impl IntoResponse for ApiError {
|
||||||
StatusCode::INSUFFICIENT_STORAGE,
|
StatusCode::INSUFFICIENT_STORAGE,
|
||||||
"DISK_FULL",
|
"DISK_FULL",
|
||||||
format!(
|
format!(
|
||||||
"IO error: {msg}\n\nDISK SPACE ISSUE DETECTED:\nThe server has run \
|
"IO error: {msg}\n\nDISK SPACE ISSUE DETECTED:\nThe server has \
|
||||||
out of disk space. Please free up space:\n- Run \
|
run out of disk space. Please free up space:\n- Run \
|
||||||
`nix-collect-garbage -d` to clean the Nix store\n- Clear the \
|
`nix-collect-garbage -d` to clean the Nix store\n- Clear the \
|
||||||
evaluator work directory: `rm -rf /tmp/fc-evaluator/*`\n- \
|
evaluator work directory: `rm -rf /tmp/fc-evaluator/*`\n- \
|
||||||
Clear build logs if configured"
|
Clear build logs if configured"
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,10 @@ async fn narinfo(
|
||||||
};
|
};
|
||||||
|
|
||||||
let nar_hash = entry.get("narHash").and_then(|v| v.as_str()).unwrap_or("");
|
let nar_hash = entry.get("narHash").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let nar_size = entry.get("narSize").and_then(serde_json::Value::as_u64).unwrap_or(0);
|
let nar_size = entry
|
||||||
|
.get("narSize")
|
||||||
|
.and_then(serde_json::Value::as_u64)
|
||||||
|
.unwrap_or(0);
|
||||||
let store_path = entry
|
let store_path = entry
|
||||||
.get("path")
|
.get("path")
|
||||||
.and_then(|v| v.as_str())
|
.and_then(|v| v.as_str())
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,19 @@ use axum::{
|
||||||
response::{Html, IntoResponse, Redirect, Response},
|
response::{Html, IntoResponse, Redirect, Response},
|
||||||
routing::get,
|
routing::get,
|
||||||
};
|
};
|
||||||
use fc_common::models::{Build, Evaluation, BuildStatus, EvaluationStatus, ApiKey, Project, Jobset, BuildStep, BuildProduct, Channel, SystemStatus};
|
use fc_common::models::{
|
||||||
|
ApiKey,
|
||||||
|
Build,
|
||||||
|
BuildProduct,
|
||||||
|
BuildStatus,
|
||||||
|
BuildStep,
|
||||||
|
Channel,
|
||||||
|
Evaluation,
|
||||||
|
EvaluationStatus,
|
||||||
|
Jobset,
|
||||||
|
Project,
|
||||||
|
SystemStatus,
|
||||||
|
};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
|
@ -699,7 +711,8 @@ async fn evaluations_page(
|
||||||
Ok(js) => {
|
Ok(js) => {
|
||||||
let pname =
|
let pname =
|
||||||
fc_common::repo::projects::get(&state.pool, js.project_id)
|
fc_common::repo::projects::get(&state.pool, js.project_id)
|
||||||
.await.map_or_else(|_| "-".to_string(), |p| p.name);
|
.await
|
||||||
|
.map_or_else(|_| "-".to_string(), |p| p.name);
|
||||||
(js.name, pname)
|
(js.name, pname)
|
||||||
},
|
},
|
||||||
Err(_) => ("-".to_string(), "-".to_string()),
|
Err(_) => ("-".to_string(), "-".to_string()),
|
||||||
|
|
@ -983,13 +996,17 @@ async fn queue_page(State(state): State<AppState>) -> Html<String> {
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
let builder_name = b.builder_id.and_then(|id| builder_map.get(&id).cloned());
|
let builder_name =
|
||||||
|
b.builder_id.and_then(|id| builder_map.get(&id).cloned());
|
||||||
QueueBuildView {
|
QueueBuildView {
|
||||||
id: b.id,
|
id: b.id,
|
||||||
job_name: b.job_name.clone(),
|
job_name: b.job_name.clone(),
|
||||||
system: b.system.clone().unwrap_or_else(|| "unknown".to_string()),
|
system: b.system.clone().unwrap_or_else(|| "unknown".to_string()),
|
||||||
created_at: b.created_at.format("%Y-%m-%d %H:%M").to_string(),
|
created_at: b.created_at.format("%Y-%m-%d %H:%M").to_string(),
|
||||||
started_at: b.started_at.map(|t| t.format("%H:%M:%S").to_string()).unwrap_or_default(),
|
started_at: b
|
||||||
|
.started_at
|
||||||
|
.map(|t| t.format("%H:%M:%S").to_string())
|
||||||
|
.unwrap_or_default(),
|
||||||
elapsed,
|
elapsed,
|
||||||
priority: b.priority,
|
priority: b.priority,
|
||||||
builder_name,
|
builder_name,
|
||||||
|
|
@ -1002,7 +1019,8 @@ async fn queue_page(State(state): State<AppState>) -> Html<String> {
|
||||||
let pending_builds: Vec<QueueBuildView> = pending
|
let pending_builds: Vec<QueueBuildView> = pending
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(idx, b)| QueueBuildView {
|
.map(|(idx, b)| {
|
||||||
|
QueueBuildView {
|
||||||
id: b.id,
|
id: b.id,
|
||||||
job_name: b.job_name.clone(),
|
job_name: b.job_name.clone(),
|
||||||
system: b.system.clone().unwrap_or_else(|| "unknown".to_string()),
|
system: b.system.clone().unwrap_or_else(|| "unknown".to_string()),
|
||||||
|
|
@ -1012,6 +1030,7 @@ async fn queue_page(State(state): State<AppState>) -> Html<String> {
|
||||||
priority: b.priority,
|
priority: b.priority,
|
||||||
builder_name: None,
|
builder_name: None,
|
||||||
queue_pos: (idx + 1) as i64,
|
queue_pos: (idx + 1) as i64,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
@ -1153,8 +1172,10 @@ async fn admin_page(
|
||||||
name: k.name,
|
name: k.name,
|
||||||
role: k.role,
|
role: k.role,
|
||||||
created_at: k.created_at.format("%Y-%m-%d %H:%M").to_string(),
|
created_at: k.created_at.format("%Y-%m-%d %H:%M").to_string(),
|
||||||
last_used_at: k
|
last_used_at: k.last_used_at.map_or_else(
|
||||||
.last_used_at.map_or_else(|| "Never".to_string(), |t| t.format("%Y-%m-%d %H:%M").to_string()),
|
|| "Never".to_string(),
|
||||||
|
|t| t.format("%Y-%m-%d %H:%M").to_string(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -1218,7 +1239,9 @@ async fn login_action(
|
||||||
password: password.clone(),
|
password: password.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(user) = fc_common::repo::users::authenticate(&state.pool, &creds).await {
|
if let Ok(user) =
|
||||||
|
fc_common::repo::users::authenticate(&state.pool, &creds).await
|
||||||
|
{
|
||||||
let session_id = Uuid::new_v4().to_string();
|
let session_id = Uuid::new_v4().to_string();
|
||||||
state
|
state
|
||||||
.sessions
|
.sessions
|
||||||
|
|
@ -1269,7 +1292,9 @@ async fn login_action(
|
||||||
hasher.update(token.as_bytes());
|
hasher.update(token.as_bytes());
|
||||||
let key_hash = hex::encode(hasher.finalize());
|
let key_hash = hex::encode(hasher.finalize());
|
||||||
|
|
||||||
if let Ok(Some(api_key)) = fc_common::repo::api_keys::get_by_hash(&state.pool, &key_hash).await {
|
if let Ok(Some(api_key)) =
|
||||||
|
fc_common::repo::api_keys::get_by_hash(&state.pool, &key_hash).await
|
||||||
|
{
|
||||||
let session_id = Uuid::new_v4().to_string();
|
let session_id = Uuid::new_v4().to_string();
|
||||||
state
|
state
|
||||||
.sessions
|
.sessions
|
||||||
|
|
@ -1280,7 +1305,8 @@ async fn login_action(
|
||||||
});
|
});
|
||||||
|
|
||||||
let cookie = format!(
|
let cookie = format!(
|
||||||
"fc_session={session_id}; HttpOnly; SameSite=Strict; Path=/; Max-Age=86400"
|
"fc_session={session_id}; HttpOnly; SameSite=Strict; Path=/; \
|
||||||
|
Max-Age=86400"
|
||||||
);
|
);
|
||||||
(
|
(
|
||||||
[(axum::http::header::SET_COOKIE, cookie)],
|
[(axum::http::header::SET_COOKIE, cookie)],
|
||||||
|
|
@ -1408,8 +1434,10 @@ async fn users_page(
|
||||||
role: u.role,
|
role: u.role,
|
||||||
user_type: user_type.to_string(),
|
user_type: user_type.to_string(),
|
||||||
enabled: u.enabled,
|
enabled: u.enabled,
|
||||||
last_login_at: u
|
last_login_at: u.last_login_at.map_or_else(
|
||||||
.last_login_at.map_or_else(|| "Never".to_string(), |t| t.format("%Y-%m-%d %H:%M").to_string()),
|
|| "Never".to_string(),
|
||||||
|
|t| t.format("%Y-%m-%d %H:%M").to_string(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -1455,12 +1483,14 @@ async fn starred_page(
|
||||||
// Get project name
|
// Get project name
|
||||||
let project_name =
|
let project_name =
|
||||||
fc_common::repo::projects::get(&state.pool, s.project_id)
|
fc_common::repo::projects::get(&state.pool, s.project_id)
|
||||||
.await.map_or_else(|_| "-".to_string(), |p| p.name);
|
.await
|
||||||
|
.map_or_else(|_| "-".to_string(), |p| p.name);
|
||||||
|
|
||||||
// Get jobset name
|
// Get jobset name
|
||||||
let jobset_name = if let Some(js_id) = s.jobset_id {
|
let jobset_name = if let Some(js_id) = s.jobset_id {
|
||||||
fc_common::repo::jobsets::get(&state.pool, js_id)
|
fc_common::repo::jobsets::get(&state.pool, js_id)
|
||||||
.await.map_or_else(|_| "-".to_string(), |j| j.name)
|
.await
|
||||||
|
.map_or_else(|_| "-".to_string(), |j| j.name)
|
||||||
} else {
|
} else {
|
||||||
"-".to_string()
|
"-".to_string()
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -403,7 +403,9 @@ async fn handle_gitea_push(
|
||||||
.map_err(ApiError)?;
|
.map_err(ApiError)?;
|
||||||
|
|
||||||
// Fall back to the other type if not found
|
// Fall back to the other type if not found
|
||||||
let webhook_config = if let Some(c) = webhook_config { c } else {
|
let webhook_config = if let Some(c) = webhook_config {
|
||||||
|
c
|
||||||
|
} else {
|
||||||
let alt = if forge_type == "gitea" {
|
let alt = if forge_type == "gitea" {
|
||||||
"forgejo"
|
"forgejo"
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue