Monorepo for Tangled tangled.org

cmd/populatepipelines: script to generate dummy pipeline runs #1056

merged opened by anirudh.fi targeting master from icy/mnwnvv
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:hwevmowznbiukdf6uk5dwrrq/sh.tangled.repo.pull/3mee2ywxa5j22
+242
Diff #0
+242
cmd/populatepipelines/populate_pipelines.go
··· 1 + package main 2 + 3 + import ( 4 + "database/sql" 5 + "flag" 6 + "fmt" 7 + "log" 8 + "math/rand" 9 + "time" 10 + 11 + "github.com/bluesky-social/indigo/atproto/syntax" 12 + _ "github.com/mattn/go-sqlite3" 13 + ) 14 + 15 + var ( 16 + dbPath = flag.String("db", "appview.db", "Path to SQLite database") 17 + count = flag.Int("count", 10, "Number of pipeline runs to generate") 18 + repo = flag.String("repo", "", "Repository name (e.g., 'did:plc:xyz/myrepo')") 19 + knot = flag.String("knot", "localhost:8100", "Knot hostname") 20 + ) 21 + 22 + // StatusKind represents the status of a workflow 23 + type StatusKind string 24 + 25 + const ( 26 + StatusKindPending StatusKind = "pending" 27 + StatusKindRunning StatusKind = "running" 28 + StatusKindFailed StatusKind = "failed" 29 + StatusKindTimeout StatusKind = "timeout" 30 + StatusKindCancelled StatusKind = "cancelled" 31 + StatusKindSuccess StatusKind = "success" 32 + ) 33 + 34 + var finishStatuses = []StatusKind{ 35 + StatusKindFailed, 36 + StatusKindTimeout, 37 + StatusKindCancelled, 38 + StatusKindSuccess, 39 + } 40 + 41 + // generateRandomSha generates a random 40-character SHA 42 + func generateRandomSha() string { 43 + const hexChars = "0123456789abcdef" 44 + sha := make([]byte, 40) 45 + for i := range sha { 46 + sha[i] = hexChars[rand.Intn(len(hexChars))] 47 + } 48 + return string(sha) 49 + } 50 + 51 + // generateRkey generates a TID-like rkey 52 + func generateRkey() string { 53 + // Simple timestamp-based rkey 54 + now := time.Now().UnixMicro() 55 + return fmt.Sprintf("%d", now) 56 + } 57 + 58 + func main() { 59 + flag.Parse() 60 + 61 + if *repo == "" { 62 + log.Fatal("--repo is required (format: did:plc:xyz/reponame)") 63 + } 64 + 65 + // Parse repo into owner and name 66 + did, repoName, ok := parseRepo(*repo) 67 + if !ok { 68 + log.Fatalf("Invalid repo format: %s (expected: did:plc:xyz/reponame)", *repo) 69 + } 70 + 71 + db, err := sql.Open("sqlite3", *dbPath) 72 + if err != nil { 73 + log.Fatalf("Failed to open database: %v", err) 74 + } 75 + defer db.Close() 76 + 77 + rand.Seed(time.Now().UnixNano()) 78 + 79 + branches := []string{"main", "develop", "feature/auth", "fix/bugs"} 80 + workflows := []string{"test", "build", "lint", "deploy"} 81 + 82 + log.Printf("Generating %d pipeline runs for %s...\n", *count, *repo) 83 + 84 + for i := 0; i < *count; i++ { 85 + // Random trigger type 86 + isPush := rand.Float32() > 0.3 // 70% push, 30% PR 87 + 88 + var triggerId int64 89 + if isPush { 90 + triggerId, err = createPushTrigger(db, branches) 91 + } else { 92 + triggerId, err = createPRTrigger(db, branches) 93 + } 94 + if err != nil { 95 + log.Fatalf("Failed to create trigger: %v", err) 96 + } 97 + 98 + // Create pipeline 99 + pipelineRkey := generateRkey() 100 + sha := generateRandomSha() 101 + createdTime := time.Now().Add(-time.Duration(rand.Intn(7*24*60)) * time.Minute) // Random time in last week 102 + 103 + _, err = db.Exec(` 104 + INSERT INTO pipelines (knot, rkey, repo_owner, repo_name, sha, created, trigger_id) 105 + VALUES (?, ?, ?, ?, ?, ?, ?) 106 + `, *knot, pipelineRkey, did, repoName, sha, createdTime.Format(time.RFC3339), triggerId) 107 + 108 + if err != nil { 109 + log.Fatalf("Failed to create pipeline: %v", err) 110 + } 111 + 112 + // Create workflow statuses 113 + numWorkflows := rand.Intn(len(workflows)-1) + 2 // 2-4 workflows 114 + selectedWorkflows := make([]string, numWorkflows) 115 + perm := rand.Perm(len(workflows)) 116 + for j := 0; j < numWorkflows; j++ { 117 + selectedWorkflows[j] = workflows[perm[j]] 118 + } 119 + 120 + for _, workflow := range selectedWorkflows { 121 + err = createWorkflowStatuses(db, *knot, pipelineRkey, workflow, createdTime) 122 + if err != nil { 123 + log.Fatalf("Failed to create workflow statuses: %v", err) 124 + } 125 + } 126 + 127 + log.Printf("Created pipeline %d/%d (rkey: %s)\n", i+1, *count, pipelineRkey) 128 + 129 + // Small delay to ensure unique rkeys 130 + time.Sleep(2 * time.Millisecond) 131 + } 132 + 133 + log.Println("✓ Pipeline population complete!") 134 + } 135 + 136 + func parseRepo(repo string) (syntax.DID, string, bool) { 137 + // Simple parser for "did:plc:xyz/reponame" 138 + for i := 0; i < len(repo); i++ { 139 + if repo[i] == '/' { 140 + did := syntax.DID(repo[:i]) 141 + name := repo[i+1:] 142 + if did != "" && name != "" { 143 + return did, name, true 144 + } 145 + } 146 + } 147 + return "", "", false 148 + } 149 + 150 + func createPushTrigger(db *sql.DB, branches []string) (int64, error) { 151 + branch := branches[rand.Intn(len(branches))] 152 + oldSha := generateRandomSha() 153 + newSha := generateRandomSha() 154 + 155 + result, err := db.Exec(` 156 + INSERT INTO triggers (kind, push_ref, push_new_sha, push_old_sha) 157 + VALUES (?, ?, ?, ?) 158 + `, "push", "refs/heads/"+branch, newSha, oldSha) 159 + 160 + if err != nil { 161 + return 0, err 162 + } 163 + 164 + return result.LastInsertId() 165 + } 166 + 167 + func createPRTrigger(db *sql.DB, branches []string) (int64, error) { 168 + targetBranch := branches[0] // Usually main 169 + sourceBranch := branches[rand.Intn(len(branches)-1)+1] 170 + sourceSha := generateRandomSha() 171 + actions := []string{"opened", "synchronize", "reopened"} 172 + action := actions[rand.Intn(len(actions))] 173 + 174 + result, err := db.Exec(` 175 + INSERT INTO triggers (kind, pr_source_branch, pr_target_branch, pr_source_sha, pr_action) 176 + VALUES (?, ?, ?, ?, ?) 177 + `, "pull_request", sourceBranch, targetBranch, sourceSha, action) 178 + 179 + if err != nil { 180 + return 0, err 181 + } 182 + 183 + return result.LastInsertId() 184 + } 185 + 186 + func createWorkflowStatuses(db *sql.DB, knot, pipelineRkey, workflow string, startTime time.Time) error { 187 + // Generate a progression of statuses for the workflow 188 + statusProgression := []StatusKind{StatusKindPending, StatusKindRunning} 189 + 190 + // Randomly choose a final status (80% success, 10% failed, 5% timeout, 5% cancelled) 191 + roll := rand.Float32() 192 + var finalStatus StatusKind 193 + switch { 194 + case roll < 0.80: 195 + finalStatus = StatusKindSuccess 196 + case roll < 0.90: 197 + finalStatus = StatusKindFailed 198 + case roll < 0.95: 199 + finalStatus = StatusKindTimeout 200 + default: 201 + finalStatus = StatusKindCancelled 202 + } 203 + 204 + statusProgression = append(statusProgression, finalStatus) 205 + 206 + currentTime := startTime 207 + for i, status := range statusProgression { 208 + rkey := fmt.Sprintf("%s-%s-%d", pipelineRkey, workflow, i) 209 + 210 + // Add some realistic time progression (10-60 seconds between statuses) 211 + if i > 0 { 212 + currentTime = currentTime.Add(time.Duration(rand.Intn(50)+10) * time.Second) 213 + } 214 + 215 + var errorMsg *string 216 + var exitCode int 217 + 218 + if status == StatusKindFailed { 219 + msg := "Command exited with non-zero status" 220 + errorMsg = &msg 221 + exitCode = rand.Intn(100) + 1 222 + } else if status == StatusKindTimeout { 223 + msg := "Workflow exceeded maximum execution time" 224 + errorMsg = &msg 225 + exitCode = 124 226 + } 227 + 228 + _, err := db.Exec(` 229 + INSERT INTO pipeline_statuses ( 230 + spindle, rkey, pipeline_knot, pipeline_rkey, 231 + created, workflow, status, error, exit_code 232 + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) 233 + `, "spindle.example.com", rkey, knot, pipelineRkey, 234 + currentTime.Format(time.RFC3339), workflow, string(status), errorMsg, exitCode) 235 + 236 + if err != nil { 237 + return err 238 + } 239 + } 240 + 241 + return nil 242 + }

History

1 round 0 comments
sign up or login to add to the discussion
anirudh.fi submitted #0
1 commit
expand
cmd/populatepipelines: script to generate dummy pipeline runs
2/3 timeout, 1/3 success
expand
expand 0 comments
pull request successfully merged