-
-
Notifications
You must be signed in to change notification settings - Fork 98
[feature/bots] Add CI failure bot for automated responses #524 #562
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
Changes from all commits
939a127
94d6de9
c64a94a
06b7141
520ede6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,10 @@ | ||
| [run] | ||
| source = openwisp_utils | ||
| parallel = true | ||
| concurrency = multiprocessing | ||
| source = | ||
| openwisp_utils | ||
| branch = True | ||
| parallel = True | ||
|
|
||
| [report] | ||
| omit = | ||
| /*/test* | ||
| /*/__init__.py | ||
| /*/migrations/* | ||
| */migrations/* | ||
| */__init__.py |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| --- | ||
| name: CI Failure Bot | ||
|
|
||
| on: | ||
| workflow_run: | ||
| workflows: ["OpenWISP Utils CI Build"] | ||
| types: | ||
| - completed | ||
|
|
||
| permissions: | ||
| issues: write | ||
| pull-requests: write | ||
| contents: read | ||
|
|
||
| jobs: | ||
| ci-failure-bot: | ||
| runs-on: ubuntu-latest | ||
| if: ${{ github.event.workflow_run.conclusion == 'failure' && !contains(github.event.workflow_run.actor.login, 'dependabot') }} | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v6 | ||
| with: | ||
| python-version: "3.11" | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| pip install -e .[github_actions] | ||
|
|
||
| - name: Run CI Failure Bot | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} | ||
| WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }} | ||
| REPOSITORY: ${{ github.repository }} | ||
| PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number || '' }} | ||
| run: | | ||
| echo "Starting CI Failure Bot..." | ||
| python -m openwisp_utils.bots.ci_failure.bot | ||
| echo "CI Failure Bot completed successfully" | ||
|
Comment on lines
+40
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Success message prints regardless of bot exit status. With 🔧 Suggested fix to conditionally print success run: |
set +e # Allow bot to handle errors internally
echo "Starting CI Failure Bot..."
python -m openwisp_utils.bots.ci_failure.bot
- echo "CI Failure Bot completed successfully"
+ exit_code=$?
+ if [ $exit_code -eq 0 ]; then
+ echo "CI Failure Bot completed successfully"
+ else
+ echo "CI Failure Bot exited with code $exit_code"
+ fi🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,6 +56,133 @@ times with a 30 second delay between attempts. | |
| attempts, the action will exit with a non-zero status, causing the | ||
| workflow to fail. | ||
|
|
||
| CI Failure Bot | ||
| ~~~~~~~~~~~~~~ | ||
|
|
||
| This GitHub workflow analyzes failed CI builds when a pull request context | ||
| is available and provides intelligent feedback to contributors using | ||
| AI-powered analysis. | ||
|
|
||
| The bot examines build logs, PR changes, and workflow context to generate | ||
| specific, actionable guidance that helps contributors fix issues quickly. | ||
|
|
||
| **Inputs** | ||
|
|
||
| - ``GEMINI_API_KEY`` (optional): Google Gemini API key for AI analysis. If | ||
| not provided, the bot uses fallback responses | ||
| - ``GEMINI_MODEL`` (optional): Gemini model to use. Defaults to | ||
| ``gemini-2.5-flash`` | ||
|
|
||
| **Usage Example** | ||
|
|
||
| You can use this workflow in your repository as follows: | ||
|
|
||
| .. code-block:: yaml | ||
|
|
||
| name: CI Failure Bot | ||
|
|
||
| on: | ||
| workflow_run: | ||
| workflows: ["OpenWISP Utils CI Build"] | ||
| types: | ||
| - completed | ||
|
|
||
| permissions: | ||
| issues: write | ||
| pull-requests: write | ||
| contents: read | ||
|
|
||
| jobs: | ||
| ci-failure-bot: | ||
| runs-on: ubuntu-latest | ||
| if: ${{ github.event.workflow_run.conclusion == 'failure' && !contains(github.event.workflow_run.actor.login, 'dependabot') }} | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v6 | ||
| with: | ||
| python-version: "3.11" | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| pip install -e .[github_actions] | ||
|
|
||
| - name: Run CI Failure Bot | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} | ||
| WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }} | ||
| REPOSITORY: ${{ github.repository }} | ||
| PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number || '' }} | ||
| run: python -m openwisp_utils.bots.ci_failure.bot | ||
|
|
||
| This example automatically triggers when the "OpenWISP Utils CI Build" | ||
| workflow fails, analyzes the failure using Gemini AI, and posts | ||
| intelligent feedback to the associated pull request. | ||
|
|
||
| **Features** | ||
|
|
||
| - **Automatic triggering**: Responds to CI build failures in pull requests | ||
| - **AI-powered analysis**: Uses Google Gemini to analyze failure logs and | ||
| provide specific guidance | ||
| - **Targeted remediation**: Suggests QA commands, test commands, or setup | ||
| fixes depending on which checks failed (no generic advice) | ||
| - **Intelligent responses**: Provides direct, actionable feedback based on | ||
| actual failure context | ||
| - **Comment deduplication**: Updates existing comments instead of creating | ||
| duplicates | ||
| - **Dependabot exclusion**: Automatically skips dependency update PRs | ||
| - **Fork detection**: Skips external PRs for security | ||
| - **Fallback handling**: Provides basic guidance if AI analysis fails | ||
|
|
||
| **Configuration** | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the formatting of this line doesn't make sense as this is bold text and in the next line there's a heading, please use a restructuredtext visualizer to preview this page so you can see the result and edit weird details. |
||
| +++++++++++++++++ | ||
|
|
||
| Repository Secrets | ||
| ++++++++++++++++++ | ||
|
|
||
| The following secrets can be configured in the repository for enhanced | ||
| functionality: | ||
|
|
||
| - ``GEMINI_API_KEY``: Google Gemini API key for AI analysis (optional - | ||
| fallback responses used if not provided) | ||
|
|
||
| Environment Variables | ||
| +++++++++++++++++++++ | ||
|
|
||
| Optional environment variables for customization: | ||
|
|
||
| - ``GEMINI_MODEL``: Gemini model to use (default: ``gemini-2.5-flash``) | ||
|
Comment on lines
+141
to
+158
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RST heading hierarchy: "Configuration", "Repository Secrets", and "Environment Variables" are siblings, not parent-child. All three headings use 📝 Suggested fix **Configuration**
+++++++++++++++++
Repository Secrets
-++++++++++++++++++
+^^^^^^^^^^^^^^^^^^
The following secrets can be configured in the repository for enhanced
functionality:
- ``GEMINI_API_KEY``: Google Gemini API key for AI analysis (optional -
fallback responses used if not provided)
Environment Variables
-+++++++++++++++++++++
+^^^^^^^^^^^^^^^^^^^^^
Optional environment variables for customization:🤖 Prompt for AI Agents |
||
|
|
||
| **Limitations** | ||
|
|
||
| - **Pull request context availability**: When triggered via | ||
| ``workflow_run``, GitHub may not always provide an associated pull | ||
| request (for example, when builds are triggered by pushes or scheduled | ||
| workflows). In these cases, the bot will not post a comment, as no pull | ||
| request context is available. | ||
| - **Optional Gemini API**: Google Gemini API access enhances analysis | ||
| quality, but the bot provides fallback responses when unavailable | ||
| - **Privacy consideration**: PR diffs and build logs are sent to Google's | ||
| Gemini AI service for analysis when API key is provided. Organizations | ||
| with sensitive codebases should review Google's data handling policies | ||
| - **API costs**: Each CI failure with Gemini enabled triggers an API call. | ||
| Monitor usage to manage costs, especially in repositories with frequent | ||
| CI failures | ||
| - Analysis quality depends on error log clarity | ||
| - May not handle very complex or unusual failure scenarios | ||
| - Skips dependabot PRs to avoid unnecessary noise | ||
|
|
||
| .. note:: | ||
|
|
||
| If the Gemini API is unavailable or analysis fails, the bot provides a | ||
| fallback response with standard OpenWISP QA guidance. Critical errors | ||
| are logged in GitHub Actions, but the workflow is designed to complete | ||
| safely without blocking contributor feedback. | ||
|
|
||
| GitHub Workflows | ||
| ---------------- | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| default_app_config = "openwisp_utils.bots.apps.BotsConfig" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| from django.apps import AppConfig | ||
|
|
||
|
|
||
| class BotsConfig(AppConfig): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are you doing? This is totally out of place! This is not a django app! |
||
| name = "openwisp_utils.bots" | ||
| label = "openwisp_utils_bots" | ||
| verbose_name = "OpenWISP Bots" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # CI Failure Bot |
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.
git diffwill always be empty: workflow_run checks out the default branch, not the PR branch.The
workflow_runevent triggers on the repository's default branch. Line 22 checks out the default branch, and then the bot runsgit diff origin/{default_branch}— comparing the default branch against itself, which always yields an empty diff.get_pr_diff()will always returnNone, so Gemini never receives PR diff context.To get the actual PR diff, either:
requests.get(pr.diff_url, ...)) instead of relying on local git, orOption 2: Checkout PR head
- name: Checkout repository uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Fetch PR branch + if: ${{ github.event.workflow_run.pull_requests[0].number }} + run: | + PR_HEAD=${{ github.event.workflow_run.head_sha }} + git fetch origin "$PR_HEAD" + git checkout "$PR_HEAD"Option 1 (API-based diff) is more robust for
workflow_runevents and was previously suggested.🤖 Prompt for AI Agents