diff --git a/crates/common/migrations/014_fix_build_stats_completed.sql b/crates/common/migrations/014_fix_build_stats_completed.sql new file mode 100644 index 0000000..e4881ab --- /dev/null +++ b/crates/common/migrations/014_fix_build_stats_completed.sql @@ -0,0 +1,17 @@ +-- Fix build_stats view and data after 'completed' -> 'succeeded' status rename + +-- Migrate any existing builds still using the old status value +UPDATE builds SET status = 'succeeded' WHERE status = 'completed'; + +-- Recreate the build_stats view to reference the new status +DROP VIEW IF EXISTS build_stats; +CREATE VIEW build_stats AS +SELECT + COUNT(*) as total_builds, + COUNT(CASE WHEN status = 'succeeded' THEN 1 END) as completed_builds, + COUNT(CASE WHEN status = 'failed' THEN 1 END) as failed_builds, + COUNT(CASE WHEN status = 'running' THEN 1 END) as running_builds, + COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending_builds, + AVG(EXTRACT(EPOCH FROM (completed_at - started_at))) as avg_duration_seconds +FROM builds +WHERE started_at IS NOT NULL; diff --git a/crates/common/src/repo/build_dependencies.rs b/crates/common/src/repo/build_dependencies.rs index b612d44..15550a7 100644 --- a/crates/common/src/repo/build_dependencies.rs +++ b/crates/common/src/repo/build_dependencies.rs @@ -81,7 +81,7 @@ pub async fn all_deps_completed(pool: &PgPool, build_id: Uuid) -> Result { let row: (i64,) = sqlx::query_as( "SELECT COUNT(*) FROM build_dependencies bd JOIN builds b ON \ bd.dependency_build_id = b.id WHERE bd.build_id = $1 AND b.status != \ - 'completed'", + 'succeeded'", ) .bind(build_id) .fetch_one(pool) diff --git a/crates/common/src/repo/builds.rs b/crates/common/src/repo/builds.rs index 4e6eda4..effd46e 100644 --- a/crates/common/src/repo/builds.rs +++ b/crates/common/src/repo/builds.rs @@ -40,7 +40,7 @@ pub async fn get_completed_by_drv_path( drv_path: &str, ) -> Result> { sqlx::query_as::<_, Build>( - "SELECT * FROM builds WHERE drv_path = $1 AND status = 'completed' LIMIT 1", + "SELECT * FROM builds WHERE drv_path = $1 AND status = 'succeeded' LIMIT 1", ) .bind(drv_path) .fetch_optional(pool) @@ -276,13 +276,13 @@ pub async fn cancel_cascade(pool: &PgPool, id: Uuid) -> Result> { } /// Restart a build by resetting it to pending state. -/// Only works for failed, completed, or cancelled builds. +/// Only works for failed, succeeded, or cancelled builds. pub async fn restart(pool: &PgPool, id: Uuid) -> Result { sqlx::query_as::<_, Build>( "UPDATE builds SET status = 'pending', started_at = NULL, completed_at = \ NULL, log_path = NULL, build_output_path = NULL, error_message = NULL, \ retry_count = retry_count + 1 WHERE id = $1 AND status IN ('failed', \ - 'completed', 'cancelled') RETURNING *", + 'succeeded', 'cancelled') RETURNING *", ) .bind(id) .fetch_optional(pool) @@ -315,7 +315,7 @@ pub async fn get_completed_by_drv_paths( } let builds = sqlx::query_as::<_, Build>( "SELECT DISTINCT ON (drv_path) * FROM builds WHERE drv_path = ANY($1) AND \ - status = 'completed' ORDER BY drv_path, completed_at DESC", + status = 'succeeded' ORDER BY drv_path, completed_at DESC", ) .bind(drv_paths) .fetch_all(pool) diff --git a/crates/common/src/repo/channels.rs b/crates/common/src/repo/channels.rs index a786232..844034c 100644 --- a/crates/common/src/repo/channels.rs +++ b/crates/common/src/repo/channels.rs @@ -153,7 +153,7 @@ pub async fn auto_promote_if_complete( ) -> Result<()> { // Check if all builds for this evaluation are completed let row: (i64, i64) = sqlx::query_as( - "SELECT COUNT(*), COUNT(*) FILTER (WHERE status = 'completed') FROM \ + "SELECT COUNT(*), COUNT(*) FILTER (WHERE status = 'succeeded') FROM \ builds WHERE evaluation_id = $1", ) .bind(evaluation_id) diff --git a/crates/server/src/routes/metrics.rs b/crates/server/src/routes/metrics.rs index 740db5e..df1fd44 100644 --- a/crates/server/src/routes/metrics.rs +++ b/crates/server/src/routes/metrics.rs @@ -98,7 +98,7 @@ async fn prometheus_metrics(State(state): State) -> Response { // Per-project build counts let per_project: Vec<(String, i64, i64)> = sqlx::query_as( - "SELECT p.name, COUNT(*) FILTER (WHERE b.status = 'completed'), COUNT(*) \ + "SELECT p.name, COUNT(*) FILTER (WHERE b.status = 'succeeded'), COUNT(*) \ FILTER (WHERE b.status = 'failed') FROM builds b JOIN evaluations e ON \ b.evaluation_id = e.id JOIN jobsets j ON e.jobset_id = j.id JOIN \ projects p ON j.project_id = p.id GROUP BY p.name", @@ -133,7 +133,7 @@ async fn prometheus_metrics(State(state): State) -> Response { output.push_str("# TYPE fc_builds_total gauge\n"); let _ = writeln!( output, - "fc_builds_total{{status=\"completed\"}} {}", + "fc_builds_total{{status=\"succeeded\"}} {}", stats.completed_builds.unwrap_or(0) ); let _ = writeln!( diff --git a/crates/server/tests/api_tests.rs b/crates/server/tests/api_tests.rs index 7aa9d27..1c094b6 100644 --- a/crates/server/tests/api_tests.rs +++ b/crates/server/tests/api_tests.rs @@ -679,8 +679,8 @@ async fn test_metrics_endpoint() { "Missing TYPE header for fc_builds_total" ); assert!( - body_str.contains("fc_builds_total{status=\"completed\"}"), - "Missing completed status label" + body_str.contains("fc_builds_total{status=\"succeeded\"}"), + "Missing succeeded status label" ); assert!( body_str.contains("fc_builds_total{status=\"failed\"}"), diff --git a/nix/tests/e2e.nix b/nix/tests/e2e.nix index c45531a..d22bb70 100644 --- a/nix/tests/e2e.nix +++ b/nix/tests/e2e.nix @@ -189,9 +189,9 @@ pkgs.testers.nixosTest { ) with subtest("Queue runner builds pending derivation"): - # Poll the E2E build until completed (queue-runner is already running) + # Poll the E2E build until succeeded (queue-runner is already running) machine.wait_until_succeeds( - f"curl -sf http://127.0.0.1:3000/api/v1/builds/{e2e_build_id} | jq -e 'select(.status==\"completed\")'", + f"curl -sf http://127.0.0.1:3000/api/v1/builds/{e2e_build_id} | jq -e 'select(.status==\"succeeded\")'", timeout=120 ) @@ -293,7 +293,7 @@ pkgs.testers.nixosTest { result = machine.succeed( f"curl -sf http://127.0.0.1:3000/api/v1/builds/{e2e_build_id} | jq -r .status" ).strip() - assert result == "completed", f"Expected completed after notification, got {result}" + assert result == "succeeded", f"Expected succeeded after notification, got {result}" with subtest("Channel auto-promotion after all builds complete"): # Create a channel tracking the E2E jobset @@ -409,17 +409,17 @@ pkgs.testers.nixosTest { machine.succeed("cd /tmp/test-flake-work && git add -A && git commit -m 'trigger notification test'") machine.succeed("cd /tmp/test-flake-work && git push origin HEAD:refs/heads/master") - # Wait for the notify-test build to complete + # Wait for the notify-test build to succeed machine.wait_until_succeeds( "curl -sf 'http://127.0.0.1:3000/api/v1/builds?job_name=notify-test' " - "| jq -e '.items[] | select(.status==\"completed\")'", + "| jq -e '.items[] | select(.status==\"succeeded\")'", timeout=120 ) # Get the build ID notify_build_id = machine.succeed( "curl -sf 'http://127.0.0.1:3000/api/v1/builds?job_name=notify-test' " - "| jq -r '.items[] | select(.status==\"completed\") | .id' | head -1" + "| jq -r '.items[] | select(.status==\"succeeded\") | .id' | head -1" ).strip() # Wait a bit for notification to dispatch @@ -428,8 +428,8 @@ pkgs.testers.nixosTest { # Verify the notification script was executed machine.wait_for_file("/var/lib/fc/notify-output") output = machine.succeed("cat /var/lib/fc/notify-output") - assert "BUILD_STATUS=success" in output or "BUILD_STATUS=completed" in output, \ - f"Expected BUILD_STATUS in notification output, got: {output}" + assert "BUILD_STATUS=success" in output, \ + f"Expected BUILD_STATUS=success in notification output, got: {output}" assert notify_build_id in output, f"Expected build ID {notify_build_id} in output, got: {output}" with subtest("Generate signing key and configure signing"): @@ -474,17 +474,17 @@ pkgs.testers.nixosTest { machine.succeed("cd /tmp/test-flake-work && git add -A && git commit -m 'trigger signing test'") machine.succeed("cd /tmp/test-flake-work && git push origin HEAD:refs/heads/master") - # Wait for the sign-test build to complete + # Wait for the sign-test build to succeed machine.wait_until_succeeds( "curl -sf 'http://127.0.0.1:3000/api/v1/builds?job_name=sign-test' " - "| jq -e '.items[] | select(.status==\"completed\")'", + "| jq -e '.items[] | select(.status==\"succeeded\")'", timeout=120 ) # Get the sign-test build ID sign_build_id = machine.succeed( "curl -sf 'http://127.0.0.1:3000/api/v1/builds?job_name=sign-test' " - "| jq -r '.items[] | select(.status==\"completed\") | .id' | head -1" + "| jq -r '.items[] | select(.status==\"succeeded\") | .id' | head -1" ).strip() # Verify the build has signed=true @@ -545,7 +545,7 @@ pkgs.testers.nixosTest { # Wait for evaluation and build machine.wait_until_succeeds( - "curl -sf 'http://127.0.0.1:3000/api/v1/builds?job_name=gc-test' | jq -e '.items[] | select(.status==\"completed\")'", + "curl -sf 'http://127.0.0.1:3000/api/v1/builds?job_name=gc-test' | jq -e '.items[] | select(.status==\"succeeded\")'", timeout=120 )