blob: a6d7874b302f52688abd070d9294bd8ef7f3d0fc [file] [log] [blame]
#!/bin/bash
# Copyright 2026 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#### CATEGORY=Build
### Build Fuchsia and attempt to fix failures with Gemini.
## usage: fx g-build [BUILD_ARGS]...
##
## This command runs the standard 'fx build' with the provided arguments.
## If the build fails, it will invoke Gemini to diagnose and attempt to
## fix the build error.
##
## All arguments are passed directly to 'fx build'. See 'fx build --help' for details.
# Ensure FUCHSIA_FX_INVOKER is set for duration of script for tool analytics.
export FUCHSIA_FX_INVOKER=gemini
trap 'unset FUCHSIA_FX_INVOKER' EXIT
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/../lib/vars.sh || exit $?
SIGINT_EXIT_CODE=130 # Exit code for SIGINT (Ctrl+C)
MAX_ATTEMPTS=3
function fix_build_with_gemini {
local -r build_output="$1"
shift
local -r gemini_prompt=$(cat <<EOF
The Fuchsia build failed. Your task is to diagnose and fix the build error.
**Output Requirements**
* Prefix output from "Implement" and "Summarize" with "[fx-gemini]".
* The output should be concise and suitable for CLI output.
**Workflow**
1. **Analyze:**
* Examine the build error messages provided below to understand the root cause.
* Examine recent code changes using \`git log -p -1\` or \`git diff HEAD\` to see the latest modifications.
* Do not modify \`tools/devshell/contrib/g-build\` itself.
2. **Plan:**
* Based on your analysis, formulate a clear plan to fix the issue.
3. **Implement:**
* Edit only the files provided from the BUILD OUTPUT section.
* Do not edit any existing comments or code not relevant to the fix.
* Adhere to the existing coding style of the file(s) being modified.
4. **Summarize:**
* Summarize the changes you made and exit the session.
* Do not run build commands. The calling script will re-run the build to verify your fix.
**BUILD OUTPUT**
${build_output}
EOF
)
local GEMINI_ALLOWED_TOOLS="write_file,search_for_files_codesearch,code_editor,ShellTool(fx),ShellTool(ls),ShellTool(jiri),ShellTool(git diff),ShellTool(git log)"
local -r jq_filter='if .type == "message" and .role == "assistant" then
.content
elif .type == "tool_use" then
"[fx-gemini] Using tool [\(.tool_name)]" +
(if .tool_name == "write_file" or .tool_name == "read_file" then
": \(.parameters.file_path // "Unknown file")"
elif .parameters.description then
": \(.parameters.description)"
else
""
end)
else
empty
end | select(length > 0)
'
set -o pipefail
/google/bin/releases/gemini-cli/tools/gemini \
-m gemini-2.5-flash \
-p "${gemini_prompt}" \
--allowed-tools "${GEMINI_ALLOWED_TOOLS}" \
--output-format stream-json 2>&1 | \
grep --line-buffered '^\{' | \
fx-command-run jq --unbuffered -r "${jq_filter}" >&2
local gemini_status=${PIPESTATUS[0]}
set +o pipefail
if [[ "${gemini_status}" -ne 0 ]]; then
echo >&2 "[fx-gemini] Gemini process failed with status ${gemini_status}."
fi
if [[ "${gemini_status}" -eq 0 ]]; then
echo >&2 "[fx-gemini] Ensuring trailing newlines in modified files..."
git diff --name-only HEAD | while read -r line; do
abs_path="$(git rev-parse --show-toplevel)/$line"
if [[ -f "$abs_path" ]] && [[ -n "$(tail -c 1 "$abs_path")" ]]; then
echo "" >> "$abs_path"
echo >&2 "[fx-gemini] Added missing newline to: $line"
fi
done
fi
}
set +e
BUILD_OUTPUT_FILE=$(mktemp)
trap 'rm -f "${BUILD_OUTPUT_FILE}"' EXIT
fx-command-run build "$@" 2>&1 | tee "${BUILD_OUTPUT_FILE}"
BUILD_STATUS=${PIPESTATUS[0]}
BUILD_OUTPUT=$(<"${BUILD_OUTPUT_FILE}")
set -e
# If the build failed, attempt to fix it with Gemini (up to 3 times)
if [[ "${BUILD_STATUS}" -ne 0 && "${BUILD_STATUS}" -ne "${SIGINT_EXIT_CODE}" ]]; then
for i in $(seq 1 $MAX_ATTEMPTS)
do
echo >&2 "[fx-gemini] Build failed. Attempting fix $i of $MAX_ATTEMPTS with Gemini..."
if fix_build_with_gemini "${BUILD_OUTPUT}" "$@"; then
# Re-run the build to check if the fix was successful
echo >&2 "[fx-gemini] Re-running build to verify fix..."
set +e
# BUILD_OUTPUT_FILE is still the one from the initial mktemp
fx-command-run build -q "$@" 2>&1 | tee "${BUILD_OUTPUT_FILE}"
new_build_status=${PIPESTATUS[0]}
BUILD_OUTPUT=$(<"${BUILD_OUTPUT_FILE}") # Update BUILD_OUTPUT for the next loop
set -e
if [[ "${new_build_status}" -eq 0 ]]; then
echo >&2 "[fx-gemini] Gemini successfully fixed the build on attempt $i."
exit 0
fi
fi
done
echo >&2 "[fx-gemini] Gemini failed to fix the build after $MAX_ATTEMPTS attempts."
exit 1
fi
exit ${BUILD_STATUS}