-
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
27
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
27 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 6f39bba
[ML] Fix analyze step depends_on and Buildkite meta-data stdin
edsavage 0608e37
[ML] Document analyze step gating edge cases and clarify log when bui…
edsavage a3f206f
[ML] Resolve Claude API key only after failed jobs are known
edsavage 1259113
[ML] Merge upstream/main (sync elasticsearchVersion / CI)
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,69 @@ | ||
| #!/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. | ||
| # If sibling steps are still running, BUILDKITE_BUILD_STATE is often "running" here | ||
| # (not "passed"); analyze_build_failure.py then exits without Claude when the API | ||
| # shows no failed/timed_out script jobs yet. Failures only in steps that start after | ||
| # this job cannot be analyzed without widening depends_on. | ||
| 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.
Uh oh!
There was an error while loading. Please reload this page.