Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions cmd/configure_projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,29 +442,43 @@ func triggerAndPoll(client *devlake.Client, blueprintID int, wait bool, timeout

fmt.Println(" Monitoring progress...")
deadline := time.Now().Add(timeout)
start := time.Now()

// clearLine erases the current in-place status line so subsequent output
// (completion banners, error messages) appears on a clean line.
clearLine := func() {
fmt.Printf("\r%s\r", strings.Repeat(" ", progressLineWidth))
}

ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()

for range ticker.C {
p, err := client.GetPipeline(pipeline.ID)
elapsed := time.Since(start).Truncate(time.Second)

if err != nil {
elapsed := time.Since(deadline.Add(-timeout)).Truncate(time.Second)
fmt.Printf(" [%s] Could not check status...\n", elapsed)
line := fmt.Sprintf("⚠️ Could not check status (%s elapsed)", elapsed)
fmt.Printf("\r%s%s", statusLinePrefix, truncatePad(line, progressLineWidth-statusLineIndent))
} else {
elapsed := time.Since(deadline.Add(-timeout)).Truncate(time.Second)
fmt.Printf(" [%s] Status: %s | Tasks: %d/%d\n", elapsed, p.Status, p.FinishedTasks, p.TotalTasks)
bar := renderBar(p.FinishedTasks, p.TotalTasks, progressBarWidth)
line := fmt.Sprintf("%s %d/%d tasks — %s (%s elapsed)", bar, p.FinishedTasks, p.TotalTasks, p.Status, elapsed)
fmt.Printf("\r%s%s", statusLinePrefix, truncatePad(line, progressLineWidth-statusLineIndent))

Comment on lines 460 to 467
switch p.Status {
case "TASK_COMPLETED":
fmt.Println(" \u2705 Data sync completed!")
clearLine()
fmt.Println(" ✅ Data sync completed!")
return nil
case "TASK_FAILED":
return fmt.Errorf("pipeline failed \u2014 check DevLake logs")
clearLine()
return fmt.Errorf("pipeline failed — check DevLake logs")
}
}

if time.Now().After(deadline) {
fmt.Println(" \u26a0\ufe0f Monitoring timed out. Pipeline is still running.")
clearLine()
fmt.Println(" ⚠️ Monitoring timed out. Pipeline is still running.")
fmt.Printf(" Check status: GET /pipelines/%d\n", pipeline.ID)
return nil
}
Expand Down
11 changes: 7 additions & 4 deletions cmd/deploy_azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,7 @@ func runDeployAzure(cmd *cobra.Command, args []string) error {
if err := azure.MySQLStart(mysqlName, azureRG); err != nil {
fmt.Printf(" ⚠️ Could not start MySQL: %v\n", err)
} else {
fmt.Println(" Waiting 30s for MySQL...")
time.Sleep(30 * time.Second)
countdown(30, "waiting for MySQL")
fmt.Println(" ✅ MySQL started")
}
} else if state != "" {
Expand Down Expand Up @@ -287,7 +286,12 @@ func runDeployAzure(cmd *cobra.Command, args []string) error {
params["acrName"] = acrName
}

deployment, err := azure.DeployBicep(azureRG, templatePath, params)
var deployment *azure.DeploymentOutput
err = spinWhile("Deploying Azure resources via Bicep (this takes several minutes)", func() error {
var innerErr error
deployment, innerErr = azure.DeployBicep(azureRG, templatePath, params)
return innerErr
})
Comment on lines +289 to +294
if err != nil {
return fmt.Errorf("Bicep deployment failed: %w", err)
}
Expand All @@ -303,7 +307,6 @@ func runDeployAzure(cmd *cobra.Command, args []string) error {
backendReady := waitForReady(deployment.BackendEndpoint, 30, 10*time.Second) == nil

if backendReady {
fmt.Println(" ✅ Backend is responding!")
fmt.Println("\n🔄 Triggering database migration...")
httpClient := &http.Client{Timeout: 5 * time.Second}
resp, err := httpClient.Get(deployment.BackendEndpoint + "/proceed-db-migration")
Expand Down
4 changes: 2 additions & 2 deletions cmd/deploy_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,8 +531,8 @@ func startLocalContainers(dir string, build bool, services ...string) (string, e

backendURLCandidates := []string{"http://localhost:8080", "http://localhost:8085"}
fmt.Println("\n⏳ Waiting for DevLake to be ready...")
fmt.Println(" Giving MySQL time to initialize (this takes ~30s on first run)...")
time.Sleep(30 * time.Second)
fmt.Println(" Giving MySQL time to initialize...")
countdown(30, "MySQL initializing")

backendURL, err := waitForReadyAny(backendURLCandidates, 36, 10*time.Second)
if err != nil {
Expand Down
18 changes: 12 additions & 6 deletions cmd/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,18 +178,20 @@ func deduplicateResults(results []ConnSetupResult) []ConnSetupResult {
// maxAttempts is exhausted. interval is the pause between attempts.
func waitForReady(baseURL string, maxAttempts int, interval time.Duration) error {
httpClient := &http.Client{Timeout: 5 * time.Second}
bar := newProgressBar(maxAttempts)
for attempt := 1; attempt <= maxAttempts; attempt++ {
bar.update(attempt, "waiting for DevLake")
resp, err := httpClient.Get(baseURL + "/ping")
if err == nil {
resp.Body.Close()
if resp.StatusCode == http.StatusOK {
fmt.Println(" ✅ DevLake is responding!")
bar.done("✅ DevLake is responding!")
return nil
}
}
fmt.Printf(" Attempt %d/%d — waiting...\n", attempt, maxAttempts)
time.Sleep(interval)
}
bar.clear()
return fmt.Errorf("DevLake not ready after %d attempts — check logs", maxAttempts)
}
Comment on lines 179 to 196

Expand All @@ -198,41 +200,45 @@ func waitForReady(baseURL string, maxAttempts int, interval time.Duration) error
// internal/devlake/discovery.go which checks both 8080 and 8085.
func waitForReadyAny(baseURLs []string, maxAttempts int, interval time.Duration) (string, error) {
httpClient := &http.Client{Timeout: 5 * time.Second}
bar := newProgressBar(maxAttempts)
for attempt := 1; attempt <= maxAttempts; attempt++ {
bar.update(attempt, "waiting for DevLake")
for _, baseURL := range baseURLs {
resp, err := httpClient.Get(baseURL + "/ping")
if err == nil {
resp.Body.Close()
if resp.StatusCode == http.StatusOK {
fmt.Println(" ✅ DevLake is responding!")
bar.done("✅ DevLake is responding!")
return baseURL, nil
}
}
}
if attempt < maxAttempts {
fmt.Printf(" Attempt %d/%d — waiting...\n", attempt, maxAttempts)
time.Sleep(interval)
}
}
bar.clear()
return "", fmt.Errorf("timed out after %d attempts", maxAttempts)
}

// waitForMigration polls until DevLake finishes database migration.
// During migration the API returns 428 (Precondition Required).
func waitForMigration(baseURL string, maxAttempts int, interval time.Duration) error {
httpClient := &http.Client{Timeout: 5 * time.Second}
bar := newProgressBar(maxAttempts)
for attempt := 1; attempt <= maxAttempts; attempt++ {
bar.update(attempt, "migrating database")
resp, err := httpClient.Get(baseURL + "/ping")
if err == nil {
resp.Body.Close()
if resp.StatusCode == http.StatusOK {
fmt.Println(" ✅ Migration complete!")
bar.done("✅ Migration complete!")
return nil
}
}
fmt.Printf(" Migrating... (%d/%d)\n", attempt, maxAttempts)
time.Sleep(interval)
}
bar.clear()
return fmt.Errorf("migration did not complete after %d attempts", maxAttempts)
}

Expand Down
Loading
Loading