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
|
||||
|
||||
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"
|
||||
ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] }
|
||||
dirs = "5.0"
|
||||
git2 = "0.18"
|
||||
|
||||
[build-dependencies]
|
||||
ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] }
|
||||
|
||||
@@ -58,6 +58,18 @@ impl Task {
|
||||
.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> {
|
||||
sqlx::query_as!(
|
||||
Task,
|
||||
|
||||
@@ -3,6 +3,44 @@ use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, Type, PgPool};
|
||||
use ts_rs::TS;
|
||||
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)]
|
||||
#[sqlx(type_name = "task_attempt_status", rename_all = "lowercase")]
|
||||
@@ -57,10 +95,34 @@ impl TaskAttempt {
|
||||
.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();
|
||||
|
||||
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,
|
||||
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)
|
||||
@@ -74,7 +136,9 @@ impl TaskAttempt {
|
||||
now
|
||||
)
|
||||
.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> {
|
||||
|
||||
Reference in New Issue
Block a user