From d868696a036c7b33d06ca7a0a4f8b2b8d1c4fc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Nogueira?= Date: Wed, 31 Jul 2024 15:57:00 +0100 Subject: [PATCH 1/3] handle multi-step states --- lib/action.ml | 4 +- lib/state.atd | 16 +++++- lib/state.ml | 57 +++++++++++++++++-- .../status.state_hide_success_test.json | 2 +- mock_states/status.commit1-02-failed.json | 18 ++++-- .../status.state_hide_success_test.json | 18 +++++- ...hide_success_test_disallowed_pipeline.json | 14 ++++- 7 files changed, 114 insertions(+), 15 deletions(-) diff --git a/lib/action.ml b/lib/action.ml index 118156b0..ebe6e559 100644 --- a/lib/action.ml +++ b/lib/action.ml @@ -182,7 +182,7 @@ module Action (Github_api : Api.Github) (Slack_api : Api.Slack) = struct | Some branch_statuses -> let has_same_status_as_prev (branch : branch) = match StringMap.find_opt branch.name branch_statuses with - | Some state when state = current_status -> true + | Some { status; _ } when status = current_status -> true | _ -> false in let branches = List.filter (Fun.negate has_same_status_as_prev) n.branches in @@ -196,7 +196,7 @@ module Action (Github_api : Api.Github) (Slack_api : Api.Slack) = struct end else Lwt.return [] in - State.set_repo_pipeline_status ctx.state repo.url ~pipeline ~branches:n.branches ~status:current_status; + State.set_repo_pipeline_status ctx.state repo.url ~pipeline n; Lwt.return recipients let partition_commit_comment (ctx : Context.t) n = diff --git a/lib/state.atd b/lib/state.atd index 8a3d7197..1e871f1d 100644 --- a/lib/state.atd +++ b/lib/state.atd @@ -3,8 +3,22 @@ type 'v map_as_object = abstract type 'v table_as_object = abstract type string_set = abstract +type ci_commit = { + sha: string; + author: string; + commit_message: string; + build_link: string option; + last_updated: string; +} + +type build_status = { + status: status_state; + ?original_failed_commit:ci_commit nullable; + ?current_failed_commit:ci_commit nullable; +} + (* A map from branch names to build statuses *) -type branch_statuses = status_state map_as_object +type branch_statuses = build_status map_as_object (* A map from pipeline names to [branch_statuses] maps. This tracks the last build state matched by the status_rules for each pipeline and diff --git a/lib/state.ml b/lib/state.ml index 47971e79..9f8643cc 100644 --- a/lib/state.ml +++ b/lib/state.ml @@ -23,10 +23,59 @@ let find_or_add_repo' state repo_url = let set_repo_state { state } repo_url repo_state = Stringtbl.replace state.repos repo_url repo_state let find_or_add_repo { state } repo_url = find_or_add_repo' state repo_url -let set_repo_pipeline_status { state } repo_url ~pipeline ~(branches : Github_t.branch list) ~status = - let set_branch_status branch_statuses = - let new_statuses = List.map (fun (b : Github_t.branch) -> b.name, status) branches in - let init = Option.default StringMap.empty branch_statuses in +let set_repo_pipeline_status { state } repo_url ~pipeline (notification : Github_t.status_notification) = + let branches = notification.branches in + let set_branch_status per_branch_statuses = + let current_fail_state = + match notification.state with + | Failure | Error -> + Some + { + State_t.sha = notification.sha; + author = notification.commit.commit.author.email; + commit_message = notification.commit.commit.message; + last_updated = notification.updated_at; + build_link = notification.target_url; + } + | _ -> None + in + let initial_build_status_state = + { State_t.status = notification.state; original_failed_commit = None; current_failed_commit = None } + in + let new_statuses = + List.map + (fun (branch : Github_t.branch) -> + let step_status = + Option.map_default + (fun all_branches_statuses -> + match StringMap.find_opt branch.name all_branches_statuses with + | Some (current_build_status : State_t.build_status) -> + let new_state = notification.state in + let original_failed_commit, current_failed_commit = + match new_state with + | Success -> None, None + (* when new jobs are pending, we keep the existing state *) + | Pending -> current_build_status.original_failed_commit, current_build_status.current_failed_commit + | Failure | Error -> + (* if we don't have a failed step yet, set it *) + (* if we have a failed build and are retrying failed jobs: *) + (* - if we retried the original commit job, update the timestamp *) + (* - if we have a different commit that is failing that step, update the new failing commit *) + match current_build_status.original_failed_commit with + | None -> current_fail_state, None + | Some original_failed_commit -> + match original_failed_commit.sha = notification.sha with + | true -> current_fail_state, current_build_status.current_failed_commit + | false -> current_build_status.original_failed_commit, current_fail_state + in + { State_t.status = new_state; original_failed_commit; current_failed_commit } + | None -> initial_build_status_state) + initial_build_status_state per_branch_statuses + in + branch.name, step_status) + branches + in + let init = Option.default StringMap.empty per_branch_statuses in Some (List.fold_left (fun m (key, data) -> StringMap.add key data m) init new_statuses) in let repo_state = find_or_add_repo' state repo_url in diff --git a/mock_payloads/status.state_hide_success_test.json b/mock_payloads/status.state_hide_success_test.json index 4275e1ba..f0c7bb3e 100644 --- a/mock_payloads/status.state_hide_success_test.json +++ b/mock_payloads/status.state_hide_success_test.json @@ -219,4 +219,4 @@ "type": "User", "site_admin": true } -} \ No newline at end of file +} diff --git a/mock_states/status.commit1-02-failed.json b/mock_states/status.commit1-02-failed.json index 3ab23c3d..48e41154 100644 --- a/mock_states/status.commit1-02-failed.json +++ b/mock_states/status.commit1-02-failed.json @@ -1,14 +1,24 @@ { "pipeline_statuses": { "buildkite/pipeline2": { - "master": "failure" + "master": { + "status": "failure", + "original_failed_commit": { + "sha": "7e0a933e9c71b4ca107680ca958ca1888d5e479b", + "author": "author@ahrefs.com", + "commit_message": "c1 message", + "build_link": [ + "Some", + "https://buildkite.com/ahrefs/monorepo/builds/181732" + ], + "last_updated": "2024-06-02T04:57:47+00:00" + } + } } }, "pipeline_commits": { "buildkite/pipeline2": { - "s1": [ - "7e0a933e9c71b4ca107680ca958ca1888d5e479b" - ], + "s1": ["7e0a933e9c71b4ca107680ca958ca1888d5e479b"], "s2": [] } } diff --git a/mock_states/status.state_hide_success_test.json b/mock_states/status.state_hide_success_test.json index b4a4b6b1..f53e0857 100644 --- a/mock_states/status.state_hide_success_test.json +++ b/mock_states/status.state_hide_success_test.json @@ -1,10 +1,24 @@ { "pipeline_statuses": { "default": { - "master": "failure" + "master": { + "status": "failure", + "original_failed_commit": { + "sha": "0d95302addd66c1816bce1b1d495ed1c93ccd478", + "author": "mail@example.org", + "commit_message": "Update README.md", + "build_link": [ + "Some", + "https://buildkite.com/org/pipeline2/builds/2" + ], + "last_updated": "2020-06-02T03:21:39+00:00" + } + } }, "buildkite/pipeline2": { - "master": "success" + "master": { + "status": "success" + } } }, "pipeline_commits": {} diff --git a/mock_states/status.state_hide_success_test_disallowed_pipeline.json b/mock_states/status.state_hide_success_test_disallowed_pipeline.json index 86cd62da..3438bf7e 100644 --- a/mock_states/status.state_hide_success_test_disallowed_pipeline.json +++ b/mock_states/status.state_hide_success_test_disallowed_pipeline.json @@ -1,7 +1,19 @@ { "pipeline_statuses": { "buildkite/pipeline2": { - "master": "failure" + "master": { + "status": "failure", + "original_failed_commit": { + "sha": "0d95302addd66c1816bce1b1d495ed1c93ccd478", + "author": "mail@example.org", + "commit_message": "Update README.md", + "build_link": [ + "Some", + "https://buildkite.com/org/pipeline2/builds/2" + ], + "last_updated": "2020-06-02T03:21:39+00:00" + } + } } }, "pipeline_commits": {} From b06f74bd0f0f5cc0faa17309229033f28e42d577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Nogueira?= Date: Wed, 31 Jul 2024 16:47:24 +0100 Subject: [PATCH 2/3] update ci step ocamlformat dep --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bf28347d..ad9d0550 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: # apt update is implicit # pinning package implicit # depext install implicit - - run: opam install . ocamlformat.0.20.1 + - run: opam install . ocamlformat.0.26.2 - name: compile run: opam exec -- make From da1b49d5f8b562aa0fea80e2fcfdcab49dd46481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Nogueira?= Date: Thu, 22 Aug 2024 15:57:22 +0000 Subject: [PATCH 3/3] inline comment --- lib/state.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/state.ml b/lib/state.ml index 9f8643cc..00b8a0a1 100644 --- a/lib/state.ml +++ b/lib/state.ml @@ -54,8 +54,9 @@ let set_repo_pipeline_status { state } repo_url ~pipeline (notification : Github let original_failed_commit, current_failed_commit = match new_state with | Success -> None, None - (* when new jobs are pending, we keep the existing state *) - | Pending -> current_build_status.original_failed_commit, current_build_status.current_failed_commit + | Pending -> + (* when new jobs are pending, we keep the existing state *) + current_build_status.original_failed_commit, current_build_status.current_failed_commit | Failure | Error -> (* if we don't have a failed step yet, set it *) (* if we have a failed build and are retrying failed jobs: *)