feat: implement proto-gen platform
- Config loader with env var expansion and validation - SQLite database with tasks and toolchain_versions tables - Gitea REST API client for repository and branch operations - K8s client for Job lifecycle management (with noop fallback for local dev) - REST API: tasks CRUD, retry, plugins, version management - Worker/scheduler with polling, concurrency control, and job tracking - Static frontend: generate, tasks, settings pages - Toolchain images: Go and Node.js Dockerfiles with entrypoint scripts - Helm chart with deployment, service, configmap, ingress, PVC - Multi-stage Dockerfile for service - Unit tests for config, database, gitclient, k8s, api, worker - E2E tests for idempotency, version switch, failure recovery
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"proto-gen/internal/models"
|
||||
)
|
||||
|
||||
func (db *DB) CreateTask(task *models.Task) error {
|
||||
now := time.Now().UTC().Format(time.RFC3339)
|
||||
_, err := db.conn.Exec(`
|
||||
INSERT INTO tasks (id, status, language, proto_repo, proto_branch, target_repo, toolchain_image, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
task.ID, task.Status, task.Language, task.ProtoRepo, task.ProtoBranch,
|
||||
task.TargetRepo, task.ToolchainImage, now, now,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DB) GetTask(id string) (*models.Task, error) {
|
||||
var t models.Task
|
||||
var createdAt, updatedAt string
|
||||
err := db.conn.QueryRow(`
|
||||
SELECT id, status, language, proto_repo, proto_branch, proto_commit, target_repo,
|
||||
toolchain_image, image_digest, result_commit, error, log, retries, created_at, updated_at
|
||||
FROM tasks WHERE id = ?`, id,
|
||||
).Scan(&t.ID, &t.Status, &t.Language, &t.ProtoRepo, &t.ProtoBranch, &t.ProtoCommit,
|
||||
&t.TargetRepo, &t.ToolchainImage, &t.ImageDigest, &t.ResultCommit,
|
||||
&t.Error, &t.Log, &t.Retries, &createdAt, &updatedAt)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("task not found: %s", id)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
|
||||
t.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (db *DB) UpdateTask(task *models.Task) error {
|
||||
now := time.Now().UTC().Format(time.RFC3339)
|
||||
_, err := db.conn.Exec(`
|
||||
UPDATE tasks SET status=?, proto_commit=?, image_digest=?, result_commit=?,
|
||||
error=?, log=?, retries=?, updated_at=?
|
||||
WHERE id=?`,
|
||||
task.Status, task.ProtoCommit, task.ImageDigest, task.ResultCommit,
|
||||
task.Error, task.Log, task.Retries, now, task.ID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DB) ListTasks(status string, limit, offset int) ([]*models.Task, error) {
|
||||
query := `SELECT id, status, language, proto_repo, proto_branch, proto_commit, target_repo,
|
||||
toolchain_image, image_digest, result_commit, error, log, retries, created_at, updated_at
|
||||
FROM tasks`
|
||||
var args []interface{}
|
||||
if status != "" {
|
||||
query += ` WHERE status = ?`
|
||||
args = append(args, status)
|
||||
}
|
||||
query += ` ORDER BY created_at DESC LIMIT ? OFFSET ?`
|
||||
args = append(args, limit, offset)
|
||||
|
||||
rows, err := db.conn.Query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var tasks []*models.Task
|
||||
for rows.Next() {
|
||||
var t models.Task
|
||||
var createdAt, updatedAt string
|
||||
if err := rows.Scan(&t.ID, &t.Status, &t.Language, &t.ProtoRepo, &t.ProtoBranch,
|
||||
&t.ProtoCommit, &t.TargetRepo, &t.ToolchainImage, &t.ImageDigest,
|
||||
&t.ResultCommit, &t.Error, &t.Log, &t.Retries, &createdAt, &updatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
|
||||
t.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
|
||||
tasks = append(tasks, &t)
|
||||
}
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
func (db *DB) GetPendingTasks(limit int) ([]*models.Task, error) {
|
||||
return db.ListTasks(string(models.StatusPending), limit, 0)
|
||||
}
|
||||
|
||||
func (db *DB) HasRunningTask(targetRepo string) (bool, error) {
|
||||
var count int
|
||||
err := db.conn.QueryRow(`SELECT COUNT(*) FROM tasks WHERE target_repo = ? AND status = 'running'`, targetRepo).Scan(&count)
|
||||
return count > 0, err
|
||||
}
|
||||
Reference in New Issue
Block a user