new worktree on attempt create
This commit is contained in:
5
AGENT.md
5
AGENT.md
@@ -68,3 +68,8 @@ When working on any task that involves changes to the backend and the frontend,
|
|||||||
# Testing your work
|
# Testing your work
|
||||||
|
|
||||||
Try to build the Typescript project after any frontend changes `npm run build`
|
Try to build the Typescript project after any frontend changes `npm run build`
|
||||||
|
|
||||||
|
# Backend data models
|
||||||
|
|
||||||
|
SQLX queries should be located in backend/src/models/\*
|
||||||
|
Use getters and setters instead of raw SQL queries where possible.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ bcrypt = "0.15"
|
|||||||
jsonwebtoken = "9.2"
|
jsonwebtoken = "9.2"
|
||||||
ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] }
|
ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] }
|
||||||
dirs = "5.0"
|
dirs = "5.0"
|
||||||
|
git2 = "0.18"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] }
|
ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] }
|
||||||
|
|||||||
@@ -58,6 +58,18 @@ impl Task {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn find_by_id(pool: &PgPool, id: Uuid) -> Result<Option<Self>, sqlx::Error> {
|
||||||
|
sqlx::query_as!(
|
||||||
|
Task,
|
||||||
|
r#"SELECT id, project_id, title, description, status as "status!: TaskStatus", created_at, updated_at
|
||||||
|
FROM tasks
|
||||||
|
WHERE id = $1"#,
|
||||||
|
id
|
||||||
|
)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn find_by_id_and_project_id(pool: &PgPool, id: Uuid, project_id: Uuid) -> Result<Option<Self>, sqlx::Error> {
|
pub async fn find_by_id_and_project_id(pool: &PgPool, id: Uuid, project_id: Uuid) -> Result<Option<Self>, sqlx::Error> {
|
||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
Task,
|
Task,
|
||||||
|
|||||||
@@ -3,6 +3,44 @@ use serde::{Deserialize, Serialize};
|
|||||||
use sqlx::{FromRow, Type, PgPool};
|
use sqlx::{FromRow, Type, PgPool};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use git2::{Repository, Error as GitError};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use super::task::Task;
|
||||||
|
use super::project::Project;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TaskAttemptError {
|
||||||
|
Database(sqlx::Error),
|
||||||
|
Git(GitError),
|
||||||
|
TaskNotFound,
|
||||||
|
ProjectNotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for TaskAttemptError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
TaskAttemptError::Database(e) => write!(f, "Database error: {}", e),
|
||||||
|
TaskAttemptError::Git(e) => write!(f, "Git error: {}", e),
|
||||||
|
TaskAttemptError::TaskNotFound => write!(f, "Task not found"),
|
||||||
|
TaskAttemptError::ProjectNotFound => write!(f, "Project not found"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for TaskAttemptError {}
|
||||||
|
|
||||||
|
impl From<sqlx::Error> for TaskAttemptError {
|
||||||
|
fn from(err: sqlx::Error) -> Self {
|
||||||
|
TaskAttemptError::Database(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<GitError> for TaskAttemptError {
|
||||||
|
fn from(err: GitError) -> Self {
|
||||||
|
TaskAttemptError::Git(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Type, Serialize, Deserialize, PartialEq, TS)]
|
#[derive(Debug, Clone, Type, Serialize, Deserialize, PartialEq, TS)]
|
||||||
#[sqlx(type_name = "task_attempt_status", rename_all = "lowercase")]
|
#[sqlx(type_name = "task_attempt_status", rename_all = "lowercase")]
|
||||||
@@ -57,10 +95,34 @@ impl TaskAttempt {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(pool: &PgPool, data: &CreateTaskAttempt, attempt_id: Uuid) -> Result<Self, sqlx::Error> {
|
pub async fn create(pool: &PgPool, data: &CreateTaskAttempt, attempt_id: Uuid) -> Result<Self, TaskAttemptError> {
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
|
|
||||||
sqlx::query_as!(
|
// First, get the task to get the project_id
|
||||||
|
let task = Task::find_by_id(pool, data.task_id)
|
||||||
|
.await?
|
||||||
|
.ok_or(TaskAttemptError::TaskNotFound)?;
|
||||||
|
|
||||||
|
// Then get the project using the project_id
|
||||||
|
let project = Project::find_by_id(pool, task.project_id)
|
||||||
|
.await?
|
||||||
|
.ok_or(TaskAttemptError::ProjectNotFound)?;
|
||||||
|
|
||||||
|
// Create the worktree using git2
|
||||||
|
let repo = Repository::open(&project.git_repo_path)?;
|
||||||
|
let worktree_path = Path::new(&data.worktree_path);
|
||||||
|
|
||||||
|
// Create the worktree directory if it doesn't exist
|
||||||
|
if let Some(parent) = worktree_path.parent() {
|
||||||
|
std::fs::create_dir_all(parent).map_err(|e| TaskAttemptError::Git(GitError::from_str(&e.to_string())))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the worktree at the specified path
|
||||||
|
let branch_name = format!("attempt-{}", attempt_id);
|
||||||
|
repo.worktree(&branch_name, worktree_path, None)?;
|
||||||
|
|
||||||
|
// Insert the record into the database
|
||||||
|
let task_attempt = sqlx::query_as!(
|
||||||
TaskAttempt,
|
TaskAttempt,
|
||||||
r#"INSERT INTO task_attempts (id, task_id, worktree_path, base_commit, merge_commit, created_at, updated_at)
|
r#"INSERT INTO task_attempts (id, task_id, worktree_path, base_commit, merge_commit, created_at, updated_at)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
@@ -74,7 +136,9 @@ impl TaskAttempt {
|
|||||||
now
|
now
|
||||||
)
|
)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await
|
.await?;
|
||||||
|
|
||||||
|
Ok(task_attempt)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn exists_for_task(pool: &PgPool, attempt_id: Uuid, task_id: Uuid, project_id: Uuid) -> Result<bool, sqlx::Error> {
|
pub async fn exists_for_task(pool: &PgPool, attempt_id: Uuid, task_id: Uuid, project_id: Uuid) -> Result<bool, sqlx::Error> {
|
||||||
|
|||||||
Reference in New Issue
Block a user