Monorepo for Tangled
Something went wrong. Try again.
6.5 kB · 242 lines
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243package main
import ( "database/sql" "flag" "fmt" "log" "math/rand" "time"
"github.com/bluesky-social/indigo/atproto/syntax" _ "github.com/mattn/go-sqlite3")
var ( dbPath = flag.String("db", "appview.db", "Path to SQLite database") count = flag.Int("count", 10, "Number of pipeline runs to generate") repo = flag.String("repo", "", "Repository name (e.g., 'did:plc:xyz/myrepo')") knot = flag.String("knot", "localhost:8100", "Knot hostname"))
// StatusKind represents the status of a workflowtype StatusKind string
const ( StatusKindPending StatusKind = "pending" StatusKindRunning StatusKind = "running" StatusKindFailed StatusKind = "failed" StatusKindTimeout StatusKind = "timeout" StatusKindCancelled StatusKind = "cancelled" StatusKindSuccess StatusKind = "success")
var finishStatuses = []StatusKind{ StatusKindFailed, StatusKindTimeout, StatusKindCancelled, StatusKindSuccess,}
// generateRandomSha generates a random 40-character SHAfunc generateRandomSha() string { const hexChars = "0123456789abcdef" sha := make([]byte, 40) for i := range sha { sha[i] = hexChars[rand.Intn(len(hexChars))] } return string(sha)}
// generateRkey generates a TID-like rkeyfunc generateRkey() string { // Simple timestamp-based rkey now := time.Now().UnixMicro() return fmt.Sprintf("%d", now)}
func main() { flag.Parse()
if *repo == "" { log.Fatal("--repo is required (format: did:plc:xyz/reponame)") }
// Parse repo into owner and name did, repoName, ok := parseRepo(*repo) if !ok { log.Fatalf("Invalid repo format: %s (expected: did:plc:xyz/reponame)", *repo) }
db, err := sql.Open("sqlite3", *dbPath) if err != nil { log.Fatalf("Failed to open database: %v", err) } defer db.Close()
rand.Seed(time.Now().UnixNano())
branches := []string{"main", "develop", "feature/auth", "fix/bugs"} workflows := []string{"test", "build", "lint", "deploy"}
log.Printf("Generating %d pipeline runs for %s...\n", *count, *repo)
for i := 0; i < *count; i++ { // Random trigger type isPush := rand.Float32() > 0.3 // 70% push, 30% PR
var triggerId int64 if isPush { triggerId, err = createPushTrigger(db, branches) } else { triggerId, err = createPRTrigger(db, branches) } if err != nil { log.Fatalf("Failed to create trigger: %v", err) }
// Create pipeline pipelineRkey := generateRkey() sha := generateRandomSha() createdTime := time.Now().Add(-time.Duration(rand.Intn(7*24*60)) * time.Minute) // Random time in last week
_, err = db.Exec(` INSERT INTO pipelines (knot, rkey, repo_owner, repo_name, sha, created, trigger_id) VALUES (?, ?, ?, ?, ?, ?, ?) `, *knot, pipelineRkey, did, repoName, sha, createdTime.Format(time.RFC3339), triggerId)
if err != nil { log.Fatalf("Failed to create pipeline: %v", err) }
// Create workflow statuses numWorkflows := rand.Intn(len(workflows)-1) + 2 // 2-4 workflows selectedWorkflows := make([]string, numWorkflows) perm := rand.Perm(len(workflows)) for j := 0; j < numWorkflows; j++ { selectedWorkflows[j] = workflows[perm[j]] }
for _, workflow := range selectedWorkflows { err = createWorkflowStatuses(db, *knot, pipelineRkey, workflow, createdTime) if err != nil { log.Fatalf("Failed to create workflow statuses: %v", err) } }
log.Printf("Created pipeline %d/%d (rkey: %s)\n", i+1, *count, pipelineRkey)
// Small delay to ensure unique rkeys time.Sleep(2 * time.Millisecond) }
log.Println("✓ Pipeline population complete!")}
func parseRepo(repo string) (syntax.DID, string, bool) { // Simple parser for "did:plc:xyz/reponame" for i := 0; i < len(repo); i++ { if repo[i] == '/' { did := syntax.DID(repo[:i]) name := repo[i+1:] if did != "" && name != "" { return did, name, true } } } return "", "", false}
func createPushTrigger(db *sql.DB, branches []string) (int64, error) { branch := branches[rand.Intn(len(branches))] oldSha := generateRandomSha() newSha := generateRandomSha()
result, err := db.Exec(` INSERT INTO triggers (kind, push_ref, push_new_sha, push_old_sha) VALUES (?, ?, ?, ?) `, "push", "refs/heads/"+branch, newSha, oldSha)
if err != nil { return 0, err }
return result.LastInsertId()}
func createPRTrigger(db *sql.DB, branches []string) (int64, error) { targetBranch := branches[0] // Usually main sourceBranch := branches[rand.Intn(len(branches)-1)+1] sourceSha := generateRandomSha() actions := []string{"opened", "synchronize", "reopened"} action := actions[rand.Intn(len(actions))]
result, err := db.Exec(` INSERT INTO triggers (kind, pr_source_branch, pr_target_branch, pr_source_sha, pr_action) VALUES (?, ?, ?, ?, ?) `, "pull_request", sourceBranch, targetBranch, sourceSha, action)
if err != nil { return 0, err }
return result.LastInsertId()}
func createWorkflowStatuses(db *sql.DB, knot, pipelineRkey, workflow string, startTime time.Time) error { // Generate a progression of statuses for the workflow statusProgression := []StatusKind{StatusKindPending, StatusKindRunning}
// Randomly choose a final status (80% success, 10% failed, 5% timeout, 5% cancelled) roll := rand.Float32() var finalStatus StatusKind switch { case roll < 0.80: finalStatus = StatusKindSuccess case roll < 0.90: finalStatus = StatusKindFailed case roll < 0.95: finalStatus = StatusKindTimeout default: finalStatus = StatusKindCancelled }
statusProgression = append(statusProgression, finalStatus)
currentTime := startTime for i, status := range statusProgression { rkey := fmt.Sprintf("%s-%s-%d", pipelineRkey, workflow, i)
// Add some realistic time progression (10-60 seconds between statuses) if i > 0 { currentTime = currentTime.Add(time.Duration(rand.Intn(50)+10) * time.Second) }
var errorMsg *string var exitCode int
if status == StatusKindFailed { msg := "Command exited with non-zero status" errorMsg = &msg exitCode = rand.Intn(100) + 1 } else if status == StatusKindTimeout { msg := "Workflow exceeded maximum execution time" errorMsg = &msg exitCode = 124 }
_, err := db.Exec(` INSERT INTO pipeline_statuses ( spindle, rkey, pipeline_knot, pipeline_rkey, created, workflow, status, error, exit_code ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) `, "spindle.example.com", rkey, knot, pipelineRkey, currentTime.Format(time.RFC3339), workflow, string(status), errorMsg, exitCode)
if err != nil { return err } }
return nil}