-
Notifications
You must be signed in to change notification settings - Fork 66
[ML] Add AI-powered build failure analysis to CI pipelines #2909
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
edsavage
wants to merge
23
commits into
elastic:main
Choose a base branch
from
edsavage:ci-build-failure-analyzer
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
58d7b33
[ML] Add AI-powered build failure analysis to CI pipelines
edsavage 7452716
[ML] Add Slack notifications to build failure analyzer
edsavage 0eda7f7
Post build failure analysis as a GitHub PR comment
edsavage 63bb9c0
Add --pr flag for testing GitHub comment posting
edsavage f87e872
[ML] Make AI failure analysis opt-in for PR builds
edsavage 5f13660
[ML] Enable native Buildkite PR comments for build failures
edsavage 1439519
[ML] Post AI analysis as PR comment via GitHub Actions
edsavage 5a038f4
[ML] Add temporary workflow to test Vault OIDC for GitHub Actions
edsavage 8907f3e
[ML] Remove temporary Vault OIDC test workflow
edsavage 902cba8
[ML] Use dynamic depends_on for analyze_build_failure step
edsavage 251e994
[ML] Always include build failure analysis in PR pipelines
edsavage 48a036f
[ML] TEMPORARY: deliberate compile error for CI testing
edsavage 3f61bcb
[ML] Fix analyze step and revert deliberate compile error
edsavage a567efb
[ML] TEMPORARY: deliberate compile error for CI testing (take 2)
edsavage 729f382
[ML] Fix analyze step Docker image and revert compile error
edsavage 038527f
[ML] TEMPORARY: deliberate compile error for CI testing (take 3)
edsavage 84766f1
[ML] Revert deliberate compile error after successful CI test
edsavage ef06f00
[ML] Make analyze step opt-in via "buildkite analyze" PR comment
edsavage 4d9f3c0
[ML] Improve Boost.Test failure detection in log extraction
edsavage 27dd2d2
[ML] TEMPORARY: deliberate test failure for CI analysis testing — wil…
edsavage 7b7a667
[ML] Revert deliberate test failure after successful analysis testing
edsavage 31285a5
Merge upstream/main into ci-build-failure-analyzer
edsavage cb0f821
[ML] Address Copilot review on build failure analyzer
edsavage File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| #!/bin/bash | ||
| # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
| # or more contributor license agreements. Licensed under the Elastic License | ||
| # 2.0 and the following additional limitation. Functionality enabled by the | ||
| # files subject to the Elastic License 2.0 may only be used in production when | ||
| # invoked by an Elasticsearch process with a license key installed that permits | ||
| # use of machine learning features. You may not use this file except in | ||
| # compliance with the Elastic License 2.0 and the foregoing additional | ||
| # limitation. | ||
|
|
||
| EXTRA_FLAGS="" | ||
| if [ "${ML_ANALYZE_PREVIOUS:-}" = "true" ]; then | ||
| EXTRA_FLAGS=" --find-previous-failure" | ||
| fi | ||
|
|
||
| cat <<EOL | ||
| steps: | ||
| - label: "Analyze build failure :mag:" | ||
| key: "analyze_build_failure" | ||
| command: | ||
| - | | ||
| set -eu | ||
| # Step-level if/build.state is evaluated at pipeline upload time, so it cannot | ||
| # reliably gate on the final build outcome. Skip at job start when the build already | ||
| # succeeded, except for the lightweight "find previous failure" pipeline. | ||
| bs="\${BUILDKITE_BUILD_STATE:-}" | ||
| if [ "\$bs" = "passed" ] && [ "\${ML_ANALYZE_PREVIOUS:-}" != "true" ]; then | ||
| echo "Build state is passed; skipping failure analysis." | ||
| exit 0 | ||
| fi | ||
| python3 dev-tools/analyze_build_failure.py --pipeline \$BUILDKITE_PIPELINE_SLUG --build \$BUILDKITE_BUILD_NUMBER${EXTRA_FLAGS} | ||
| EOL | ||
|
|
||
| # Emit depends_on dynamically — ML_BUILD_STEP_KEYS and ML_TEST_STEP_KEYS are | ||
| # comma-separated lists set by the pipeline generator (branch builds expose | ||
| # both; PR pipelines may only set ML_BUILD_STEP_KEYS). In analyze-previous | ||
| # mode there are no build/test steps so this block is skipped. | ||
| DEPENDS_ON_KEYS=() | ||
| if [ -n "${ML_BUILD_STEP_KEYS:-}" ]; then | ||
| IFS=',' read -ra STEP_KEYS <<< "$ML_BUILD_STEP_KEYS" | ||
| DEPENDS_ON_KEYS+=("${STEP_KEYS[@]}") | ||
| fi | ||
| if [ -n "${ML_TEST_STEP_KEYS:-}" ]; then | ||
| IFS=',' read -ra STEP_KEYS <<< "$ML_TEST_STEP_KEYS" | ||
| DEPENDS_ON_KEYS+=("${STEP_KEYS[@]}") | ||
| fi | ||
| if [ "${#DEPENDS_ON_KEYS[@]}" -gt 0 ]; then | ||
| echo ' depends_on:' | ||
| seen=" " | ||
| for key in "${DEPENDS_ON_KEYS[@]}"; do | ||
| [ -z "$key" ] && continue | ||
| case "$seen" in | ||
| *" ${key} "*) continue ;; | ||
| esac | ||
| seen+=" ${key} " | ||
| echo " - \"${key}\"" | ||
| done | ||
| fi | ||
|
|
||
| cat <<'EOL' | ||
| allow_dependency_failure: true | ||
| soft_fail: true | ||
| agents: | ||
| image: "python:3" | ||
| EOL | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| name: Post Build Failure Analysis | ||
|
|
||
| # Triggered by commit status updates from Buildkite. When the | ||
| # analyze_build_failure step completes, Buildkite posts a commit status | ||
| # which fires this workflow. We fetch the AI analysis from Buildkite | ||
| # build metadata and post it as a PR comment using the built-in | ||
| # GITHUB_TOKEN (no PAT or GitHub App needed). | ||
|
|
||
| on: | ||
| status: | ||
|
|
||
| permissions: | ||
| pull-requests: write | ||
| statuses: read | ||
|
|
||
| jobs: | ||
| post-analysis: | ||
| # Only run when the analyze step succeeds (soft_fail means Buildkite | ||
| # reports success even if the analysis itself had issues). | ||
| if: >- | ||
| github.event.state == 'success' && | ||
| contains(github.event.context, 'Analyze build failure') | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Find PR for commit | ||
| id: find-pr | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| SHA: ${{ github.event.sha }} | ||
| run: | | ||
| PR_NUMBER=$(gh api "repos/${{ github.repository }}/commits/${SHA}/pulls" \ | ||
| --jq '.[0].number // empty' 2>/dev/null || true) | ||
| if [ -z "$PR_NUMBER" ]; then | ||
| echo "No PR found for commit ${SHA} — skipping." | ||
| echo "skip=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "Found PR #${PR_NUMBER}" | ||
| echo "skip=false" >> "$GITHUB_OUTPUT" | ||
| echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Extract Buildkite build info | ||
| if: steps.find-pr.outputs.skip != 'true' | ||
| id: bk-info | ||
| env: | ||
| TARGET_URL: ${{ github.event.target_url }} | ||
| run: | | ||
| # target_url looks like: | ||
| # https://buildkite.com/elastic/ml-cpp-pr-builds/builds/2361#step-key | ||
| # Extract pipeline slug and build number. | ||
| PIPELINE=$(echo "$TARGET_URL" | sed -n 's|.*/elastic/\([^/]*\)/builds/.*|\1|p') | ||
| BUILD_NUM=$(echo "$TARGET_URL" | sed -n 's|.*/builds/\([0-9]*\).*|\1|p') | ||
| if [ -z "$PIPELINE" ] || [ -z "$BUILD_NUM" ]; then | ||
| echo "Could not parse Buildkite URL: $TARGET_URL" | ||
| echo "skip=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "Pipeline: $PIPELINE, Build: $BUILD_NUM" | ||
| echo "skip=false" >> "$GITHUB_OUTPUT" | ||
| echo "pipeline=${PIPELINE}" >> "$GITHUB_OUTPUT" | ||
| echo "build_num=${BUILD_NUM}" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Fetch analysis from Buildkite | ||
| if: >- | ||
| steps.find-pr.outputs.skip != 'true' && | ||
| steps.bk-info.outputs.skip != 'true' | ||
| id: fetch | ||
| env: | ||
| BK_TOKEN: ${{ secrets.BUILDKITE_API_READ_TOKEN }} | ||
| PIPELINE: ${{ steps.bk-info.outputs.pipeline }} | ||
| BUILD_NUM: ${{ steps.bk-info.outputs.build_num }} | ||
| run: | | ||
| if [ -z "$BK_TOKEN" ]; then | ||
| echo "BUILDKITE_API_READ_TOKEN secret not set — skipping." | ||
| echo "skip=true" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
|
|
||
| # Fetch build metadata containing the analysis (API returns JSON with a value field). | ||
| ANALYSIS_JSON=$(curl -sS -f \ | ||
| -H "Authorization: Bearer ${BK_TOKEN}" \ | ||
| "https://api.buildkite.com/v2/organizations/elastic/pipelines/${PIPELINE}/builds/${BUILD_NUM}/meta-data/build-failure-analysis" \ | ||
| 2>/dev/null) || true | ||
|
|
||
| if [ -z "$ANALYSIS_JSON" ]; then | ||
| echo "No analysis metadata found — skipping." | ||
| echo "skip=true" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
|
|
||
| ANALYSIS=$(printf '%s' "$ANALYSIS_JSON" | jq -r \ | ||
| 'if type == "string" then . elif has("value") then .value else empty end' 2>/dev/null || true) | ||
| if [ -z "$ANALYSIS" ]; then | ||
| ANALYSIS="$ANALYSIS_JSON" | ||
| fi | ||
|
|
||
| if [ -z "$ANALYSIS" ]; then | ||
| echo "Analysis metadata did not contain a value — skipping." | ||
| echo "skip=true" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
|
|
||
| printf '%s\n' "$ANALYSIS" > /tmp/analysis.md | ||
| echo "skip=false" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Post or update PR comment | ||
| if: >- | ||
| steps.find-pr.outputs.skip != 'true' && | ||
| steps.bk-info.outputs.skip != 'true' && | ||
| steps.fetch.outputs.skip != 'true' | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| PR_NUMBER: ${{ steps.find-pr.outputs.pr_number }} | ||
| PIPELINE: ${{ steps.bk-info.outputs.pipeline }} | ||
| BUILD_NUM: ${{ steps.bk-info.outputs.build_num }} | ||
| run: | | ||
| MARKER="<!-- build-failure-analysis -->" | ||
| BUILD_URL="https://buildkite.com/elastic/${PIPELINE}/builds/${BUILD_NUM}" | ||
|
|
||
| { | ||
| printf '%s\n\n' "$MARKER" | ||
| printf '## :mag: Build Failure Analysis\n\n' | ||
| cat /tmp/analysis.md | ||
| printf '\n\n---\n[View Buildkite build](%s) | *Analysis generated by Claude. Verify before acting.*\n' "$BUILD_URL" | ||
| } > /tmp/pr-body.md | ||
|
|
||
| # Check for an existing comment to update. | ||
| EXISTING_ID=$(gh api "repos/${{ github.repository }}/issues/${PR_NUMBER}/comments?per_page=100" \ | ||
| --jq ".[] | select(.body | contains(\"${MARKER}\")) | .id" 2>/dev/null | head -1) | ||
|
|
||
| if [ -n "$EXISTING_ID" ]; then | ||
| jq -n --rawfile b /tmp/pr-body.md '{body: $b}' \ | ||
| | gh api "repos/${{ github.repository }}/issues/comments/${EXISTING_ID}" \ | ||
| -X PATCH --input - | ||
| echo "Updated existing comment on PR #${PR_NUMBER}." | ||
| else | ||
| jq -n --rawfile b /tmp/pr-body.md '{body: $b}' \ | ||
| | gh api "repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" \ | ||
| -X POST --input - | ||
| echo "Posted new comment on PR #${PR_NUMBER}." | ||
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The step definition doesn’t include the conditional mentioned in the PR description (only run when the build is failing). As written, the analysis step will run on successful builds too (and require secrets / call Buildkite APIs), which is unnecessary and could incur external API cost. Add an
if:guard on the step (e.g.,build.state == 'failed' || build.state == 'failing') or an equivalent gating mechanism.