diff --git a/.buildkite/claude-analysis.yml b/.buildkite/claude-analysis.yml new file mode 100644 index 000000000000..7bde6d19b87b --- /dev/null +++ b/.buildkite/claude-analysis.yml @@ -0,0 +1,37 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/buildkite/pipeline-schema/main/schema.json +--- + +# This pipeline is dynamically uploaded at the end of the build after a wait step + +agents: + queue: default + +steps: + - label: ":claude: Claude Build Analysis" + key: claude-analysis + if: build.state == "failing" + soft_fail: true + command: "exit 1" + artifact_paths: + - "/tmp/claude_debug_${BUILDKITE_BUILD_ID}.txt" + - "/tmp/claude_response_${BUILDKITE_BUILD_ID}.json" + - "/tmp/buildkite_logs_${BUILDKITE_BUILD_ID}.txt" + plugins: + - $CLAUDE_PLUGIN: + api_key: "$ANTHROPIC_API_KEY" + buildkite_api_token: "$BUILDKITE_TOKEN_FOR_CLAUDE" + analysis_level: "build" + build_log_mode: "failed" + trigger: "on-failure" + max_log_lines: 1500 + custom_prompt: "Do not mention successful points, focus on failures and recovery to keep the analysis succinct. Only add the `Best Practices` section if the changes in this PR are directly relevant to it. Only add a `Root Cause` section when you're sure about the issues' causes." + model: "claude-sonnet-4-5" + + - label: ":claude: 💬 Comment Claude Analysis" + command: .buildkite/commands/comment-claude-analysis.sh + depends_on: claude-analysis + allow_dependency_failure: true + if: build.pull_request.id != null + plugins: + - $CI_TOOLKIT_PLUGIN + diff --git a/.buildkite/commands/comment-claude-analysis.sh b/.buildkite/commands/comment-claude-analysis.sh new file mode 100755 index 000000000000..9c23ae99e0c9 --- /dev/null +++ b/.buildkite/commands/comment-claude-analysis.sh @@ -0,0 +1,15 @@ +#!/bin/bash -eu + +# The claude-analysis step only runs when there are build failures, +# so if it ran (and soft_failed), it means there were failures that Claude analyzed. +CLAUDE_OUTCOME=$(buildkite-agent step get outcome --step claude-analysis 2>/dev/null || echo "not_run") + +if [[ "${CLAUDE_OUTCOME}" == "soft_failed" ]]; then + comment_on_pr --id claude-build-analysis "## 🤖 Build Failure Analysis + +This build has failures. Claude has analyzed them - check the build annotations for details." +else + # Remove the comment if the build is now passing (claude-analysis did not run) + comment_on_pr --id claude-build-analysis --if-exist delete +fi + diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 76ee6479759f..109db5f1e597 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -37,6 +37,7 @@ steps: depends_on: ~ - label: ":eyeglasses: Reader one-off TestFlight Upload" + key: testflight_upload_reader depends_on: [build_asc_reader, testflight_triggered_reader] command: | if .buildkite/commands/should-skip-job.sh --job-type build; then @@ -53,6 +54,7 @@ steps: # Create Prototype Builds for WP and JP ################# - group: "🛠 Prototype Builds" + key: prototype_builds_group steps: - label: "🛠 WordPress Prototype Build" command: ".buildkite/commands/prototype-build-wordpress.sh" @@ -95,6 +97,7 @@ steps: # Run Unit Tests ################# - group: "🔬 Unit Tests" + key: unit_tests_group steps: - label: "🔬 :wordpress: Unit Tests" command: ".buildkite/commands/run-unit-tests.sh" @@ -113,19 +116,15 @@ steps: notify: - github_commit_status: context: "Reader Unit Tests" - - label: "🔬 Keystone Unit Tests" - command: .buildkite/commands/run-unit-tests-for-scheme.sh Keystone - # Disabled till https://github.com/wordpress-mobile/WordPress-iOS/pull/24537 is completed - # - # The boolean value has to be a string explicitly. - # See validation error against schema. - if: "false" - plugins: [$CI_TOOLKIT_PLUGIN] - artifact_paths: - - "build/results/*" - notify: - - github_commit_status: - context: "Unit Tests Keystone" + # Disabled till https://github.com/wordpress-mobile/WordPress-iOS/pull/24537 is completed + # - label: "🔬 Keystone Unit Tests" + # command: .buildkite/commands/run-unit-tests-for-scheme.sh Keystone + # plugins: [$CI_TOOLKIT_PLUGIN] + # artifact_paths: + # - "build/results/*" + # notify: + # - github_commit_status: + # context: "Unit Tests Keystone" - label: "🔬 WordPressData Unit Tests" command: .buildkite/commands/run-unit-tests-for-scheme.sh WordPressData plugins: [$CI_TOOLKIT_PLUGIN] @@ -139,6 +138,7 @@ steps: # UI Tests ################# - group: "🔬 UI Tests" + key: ui_tests_group steps: - label: "🔬 :jetpack: UI Tests (iPhone)" command: .buildkite/commands/run-ui-tests.sh 'iPhone 17' @@ -168,6 +168,7 @@ steps: # Linters ################# - group: "Linters" + key: linters_group steps: - label: "☢️ Danger - PR Check" command: danger @@ -201,3 +202,21 @@ steps: - label: ":sleuth_or_spy: Lint Localized Strings Format" command: .buildkite/commands/lint-localized-strings-format.sh plugins: [$CI_TOOLKIT_PLUGIN] + + ################# + # Claude Build Analysis - dynamically uploaded so Build result conditions evaluate at runtime after the wait + ################# + - label: ":claude: 🕵️ Check for Build Failures and Run Claude Analysis" + command: | + source .buildkite/shared-pipeline-vars + buildkite-agent pipeline upload .buildkite/claude-analysis.yml + # Add specific group dependencies. Using a `wait` step wouldn't work as it would never start given the prompt block + depends_on: + - build_asc_reader + - linters_group + - prototype_builds_group + - unit_tests_group + - ui_tests_group + allow_dependency_failure: true + agents: + queue: upload diff --git a/.buildkite/shared-pipeline-vars b/.buildkite/shared-pipeline-vars index 300d5deea846..ac2cb0ac30cc 100755 --- a/.buildkite/shared-pipeline-vars +++ b/.buildkite/shared-pipeline-vars @@ -9,3 +9,4 @@ CI_TOOLKIT_PLUGIN_VERSION="5.3.1" export IMAGE_ID="xcode-$XCODE_VERSION" export CI_TOOLKIT_PLUGIN="automattic/a8c-ci-toolkit#$CI_TOOLKIT_PLUGIN_VERSION" +export CLAUDE_PLUGIN="iangmaia/claude-summarize#iangmaia/add-build-log-mode"