initial commit

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I79a875e75937ff6b3739ca36bfb0b2836a6a6964
This commit is contained in:
raf 2025-11-02 18:43:07 +03:00
commit 6203ea7f52
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
13 changed files with 3226 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
target/
# Nix
result*
result-*
# Misc
.direnv

2931
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

31
Cargo.toml Normal file
View file

@ -0,0 +1,31 @@
[workspace]
members = [
"crates/server",
"crates/evaluator",
"crates/queue-runner",
"crates/common",
]
resolver = "3"
[workspace.package]
version = "0.1.0"
edition = "2024"
license = "MPL-2.0"
repository = "https://gitub.com/feel-co/fc"
authors = ["NotAShelf <raf@notashelf.dev"]
[workspace.dependencies]
tokio = { version = "1.48.0", features = ["full"] }
axum = "0.8.6"
sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid"] }
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
uuid = { version = "1.18.1", features = ["v4", "serde"] }
chrono = { version = "0.4.42", features = ["serde"] }
tracing = "0.1.41"
tracing-subscriber = "0.3.20"
anyhow = "1.0.100"
thiserror = "2.0.17"
git2 = "0.20.2"
clap = { version = "4.5.51", features = ["derive"] }
config = "0.15.18"

17
crates/common/Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "fc-common"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
sqlx.workspace = true
serde.workspace = true
serde_json.workspace = true
uuid.workspace = true
chrono.workspace = true
anyhow.workspace = true
thiserror.workspace = true
git2.workspace = true

View file

@ -0,0 +1,29 @@
//! Error types for FC CI
use thiserror::Error;
#[derive(Error, Debug)]
pub enum CiError {
#[error("Database error: {0}")]
Database(#[from] sqlx::Error),
#[error("Git error: {0}")]
Git(#[from] git2::Error),
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Configuration error: {0}")]
Config(String),
#[error("Build error: {0}")]
Build(String),
#[error("Not found: {0}")]
NotFound(String),
}
pub type Result<T> = std::result::Result<T, CiError>;

7
crates/common/src/lib.rs Normal file
View file

@ -0,0 +1,7 @@
//! Common types and utilities for CI
pub mod error;
pub mod models;
pub use error::*;
pub use models::*;

View file

@ -0,0 +1,67 @@
//! Data models for CI
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Project {
pub id: Uuid,
pub name: String,
pub description: Option<String>,
pub repository_url: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Jobset {
pub id: Uuid,
pub project_id: Uuid,
pub name: String,
pub nix_expression: String,
pub enabled: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Evaluation {
pub id: Uuid,
pub jobset_id: Uuid,
pub commit_hash: String,
pub evaluation_time: DateTime<Utc>,
pub status: EvaluationStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::Type)]
#[sqlx(type_name = "text", rename_all = "lowercase")]
pub enum EvaluationStatus {
Pending,
Running,
Completed,
Failed,
}
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Build {
pub id: Uuid,
pub evaluation_id: Uuid,
pub job_name: String,
pub drv_path: String,
pub status: BuildStatus,
pub started_at: Option<DateTime<Utc>>,
pub completed_at: Option<DateTime<Utc>>,
pub log_path: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::Type)]
#[sqlx(type_name = "text", rename_all = "lowercase")]
pub enum BuildStatus {
Pending,
Running,
Completed,
Failed,
Cancelled,
}

View file

@ -0,0 +1,23 @@
[package]
name = "fc-evaluator"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
tokio.workspace = true
sqlx.workspace = true
serde.workspace = true
serde_json.workspace = true
uuid.workspace = true
chrono.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
anyhow.workspace = true
thiserror.workspace = true
git2.workspace = true
clap.workspace = true
config.workspace = true
fc-common = { path = "../common" }

View file

@ -0,0 +1,23 @@
use clap::Parser;
use tracing_subscriber::fmt::init;
#[derive(Parser)]
#[command(name = "fc-evaluator")]
#[command(about = "CI Evaluator - Git polling and Nix evaluation")]
struct Cli {
#[arg(short, long)]
config: Option<String>,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
#[allow(unused_variables, reason = "Main application logic is TODO")]
let cli = Cli::parse();
tracing::info!("Starting CI Evaluator");
init();
// TODO: Implement evaluator logic
Ok(())
}

View file

@ -0,0 +1,22 @@
[package]
name = "fc-queue-runner"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
tokio.workspace = true
sqlx.workspace = true
serde.workspace = true
serde_json.workspace = true
uuid.workspace = true
chrono.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
anyhow.workspace = true
thiserror.workspace = true
clap.workspace = true
config.workspace = true
fc-common = { path = "../common" }

View file

@ -0,0 +1,23 @@
use clap::Parser;
use tracing_subscriber::fmt::init;
#[derive(Parser)]
#[command(name = "fc-queue-runner")]
#[command(about = "CI Queue Runner - Build dispatch and execution")]
struct Cli {
#[arg(short, long)]
workers: Option<usize>,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
#[allow(unused_variables, reason = "Main application logic is TODO")]
let cli = Cli::parse();
tracing::info!("Starting CI Queue Runner");
init();
// TODO: Implement queue runner logic
Ok(())
}

23
crates/server/Cargo.toml Normal file
View file

@ -0,0 +1,23 @@
[package]
name = "fc-server"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
tokio.workspace = true
axum.workspace = true
sqlx.workspace = true
serde.workspace = true
serde_json.workspace = true
uuid.workspace = true
chrono.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
anyhow.workspace = true
thiserror.workspace = true
clap.workspace = true
config.workspace = true
fc-common = { path = "../common" }

22
crates/server/src/main.rs Normal file
View file

@ -0,0 +1,22 @@
use clap::Parser;
use tracing_subscriber::fmt::init;
#[derive(Parser)]
#[command(name = "fc-server")]
#[command(about = "CI Server - Web API and UI")]
struct Cli {
#[arg(short, long, default_value = "3000")]
port: u16,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
tracing::info!("Starting CI Server on port {}", cli.port);
init();
// TODO: Implement server logic
Ok(())
}