Devin’s API is a powerful tool designed to automatically resolve code issues detected by code scanning tools such as SonarQube. While this documentation focuses on SonarQube, the underlying strategy applies to other code scanning tools, including Veracode, Orca, and more.

If you’re interested in seeing this process in action, check out our demo repository and explore the detailed explanation in the Cognition blog post.

In production, the process will look like the following:

Automated Issue Resolution with Devin’s API

In production, the process of automatically resolving code issues using Devin’s API is streamlined and efficient. Below is a high-level overview of the steps involved:

1. Process Overview

  1. Pull Request is opened: A pull request (PR) is submitted to the repository with changes that may contain issues identified by a code scanning tool.

  2. GitHub Action is triggered: The opening of the PR automatically triggers a GitHub Action workflow.

  3. GitHub Action calls Devin API: The GitHub Action sends a request to the Devin API, passing the identified issues for automated resolution.

  4. Devin session is initialized: A Devin session spins up, receiving the context of the issue and attempting to resolve it based on the provided data.

  5. Devin proposes PR for human review: Once the issue is resolved, Devin generates a PR with the proposed changes and submits it for human review.

2. Steps to Accomplish This

To integrate Devin’s API with your CI/CD pipeline, you will need to complete the following configurations:

  1. Configure SonarQube for CI & API support:

    • Ensure that SonarQube is configured to support continuous integration (CI) and API integration. If you prefer not to set up SonarQube for API access, you can use a cookie for authentication. Learn more about this setup here.
  2. Configure GitHub environment to hold the required secrets:

    • Set up the GitHub environment to securely store the necessary secrets, such as authentication tokens and configuration keys, to interact with Devin’s API and other integrated tools.

Once these steps are complete, your pipeline will be ready to automatically resolve issues using Devin’s API, speeding up the process and reducing the need for manual intervention.

  1. Test the Integration

Once your setup is complete, you can test the integration by manually triggering a GitHub Action. This will allow you to verify that the action correctly calls the Devin API and resolves the identified issues.

  1. View Devin Sessions Page

After the GitHub Action is triggered and Devin processes the issues, you can view the status and results on the Devin sessions page. This page provides detailed insights into the resolved issues and the proposed changes.

In Depth Guide

Please skip ahead, if your SonarQube project is already appropriately configured. Otherwise the following walks through how to ensure GitHub has access to SonarQube’s API.

Required Values from SonarQube To configure the integration, you will need to obtain the following three values from your

SonarQube instance: You will need three_values from SonarQube:

1. Create SONAR_TOKEN:

  1. Click on your account icon in the top right of SonarQube.
  2. Select Security from the dropdown.
  3. Under Tokens, click Generate Tokens.
  4. Name the token and click Generate.
  • Copy the generated token for use in GitHub Actions.

2. Create SONAR_PROJECT

  1. Select the project in SonarQube.
  2. Click Information in the bottom left.
  3. Scroll down to find the project key.

3. Create SONAR_ORG

Refer to the steps above to locate your organization details in SonarQube.

Once you have all the required values, you’re ready to configure the GitHub Action.

This assumes you have a local SonarCloud properties file sonar-project.properties that specifies:

sonar.projectKey={PROJECT_KEY}
sonar.sources=FILE_PATH (usually ".")

The Github Action has the following source code

name: SonarCloud Scan and Devin Remediation
on:
  workflow_dispatch:
  push:
    branches:
      - '**'
  pull_request:
    branches:
      - '**'

jobs:
  analyze:
    name: Analyze and Remediate
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: SonarCloud Scan
      uses: SonarSource/sonarqube-scan-action@v4
      env:
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        SONAR_ORG: ${{ secrets.SONAR_ORG }}
      with:
        args: >
          -Dsonar.organization=${{ env.SONAR_ORG }}
          -Dsonar.sources=.
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.x'

    - name: Install Dependencies
      run: pip install aiohttp

    - name: Configure Git
      run: |
        git config --global user.name "GitHub Action"
        git config --global user.email "action@github.com"
    - name: Run Devin Remediation
      env:
        DEVIN_API_KEY: ${{ secrets.DEVIN_API_KEY }}
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        SONAR_ORG: ${{ secrets.SONAR_ORG }}
        SONAR_PROJECT_KEY: {SONAR_PROJECT_KEY}
      run: python .github/scripts/devin_remediation.py

As a reminder devin_remediation.py

import asyncio
import aiohttp
import os
from datetime import datetime

# Environment variables
GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY")
SONAR_TOKEN = os.getenv("SONAR_TOKEN")
DEVIN_API_KEY = os.getenv("DEVIN_API_KEY")
SONAR_ORG = os.getenv("SONAR_ORG")
SONAR_PROJECT_KEY = os.getenv("SONAR_PROJECT_KEY")
DEVIN_API_BASE = "https://api.devin.ai/v1"

async def get_sonarcloud_issues():
    """Fetch open vulnerabilities from SonarCloud."""
    url = "https://sonarcloud.io/api/issues/search"
    headers = {"Authorization": f"Bearer {SONAR_TOKEN}"}
    params = {
        "organization": SONAR_ORG,
        "projectKeys": SONAR_PROJECT_KEY,
        "types": "VULNERABILITY",
        "statuses": "OPEN"
    }

    async with aiohttp.ClientSession() as session:
        async with session.get(url, headers=headers, params=params) as response:
            if response.status != 200:
                print(f"Error getting SonarCloud issues: {await response.text()}")
                return []
            result = await response.json()
            print(f"Found {len(result.get('issues', []))} issues")
            return result.get('issues', [])

async def delegate_task_to_devin(issue):
    """Delegate the entire task of fixing, committing, and pushing to Devin AI."""
    async with aiohttp.ClientSession() as session:
        headers = {"Authorization": f"Bearer {DEVIN_API_KEY}"}
        prompt = f"""
        Fix the following vulnerability in {GITHUB_REPOSITORY}: {issue['message']} in file {issue['component']}.
        1. Create a new branch named 'devin/{issue['key']}-fix-vulnerability'.
        2. Implement the fix.
        3. Write a detailed commit message explaining the changes:
            - Issue Key: {issue['key']}
            - Component: {issue['component']}
            - Fixed by Devin AI at {datetime.now().isoformat()}
            - Include 'Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>'.
        4. Push the branch to the remote repository.
        5. Open a pull request with a description of the fix.
        """

        data = {"prompt": prompt, "idempotent": True}

        async with session.post(f"{DEVIN_API_BASE}/sessions", json=data, headers=headers) as response:
            if response.status != 200:
                print(f"Error delegating task to Devin: {await response.text()}")
                return None
            result = await response.json()
            print(f"Devin session created: {result}")
            return result

async def monitor_devin_session(session_id):
    """Monitor Devin's progress until it completes the task."""
    async with aiohttp.ClientSession() as session:
        headers = {"Authorization": f"Bearer {DEVIN_API_KEY}"}

        while True:
            async with session.get(f"{DEVIN_API_BASE}/session/{session_id}", headers=headers) as response:
                if response.status != 200:
                    print(f"Error monitoring Devin session: {await response.text()}")
                    return None

                result = await response.json()
                status = result.get("status_enum")

                if status in ["completed", "stopped"]:
                    print(f"Devin completed the task: {result}")
                    return result
                elif status == "blocked":
                    print("Devin encountered an issue. Please check manually.")
                    return None

                await asyncio.sleep(5)

async def main():
    try:
        issues = await get_sonarcloud_issues()

        for issue in issues:
            print(f"Processing issue: {issue['key']}")

            # Delegate task to Devin AI
            session_data = await delegate_task_to_devin(issue)

            if session_data:
                session_id = session_data["session_id"]

                # Monitor Devin's progress
                await monitor_devin_session(session_id)

    except Exception as e:
        print(f"Error occurred: {str(e)}")
        raise

if __name__ == "__main__":
    asyncio.run(main())

To ensure the GitHub Action sets the correct environment variables, add them to GitHub Repository Secrets.

Navigating to the correct settings can be tricky. Go to Security and edit Secrets. Add SONAR_TOKEN and DEVINS_API under Repository Secrets.

Once configured, you can monitor your GitHub Action in progress. If it runs successfully, it will appear as follows:

You can view Devin sessions in the Session Manager.

Once completed, Devin will automatically open pull requests. For GitLab users, refer to the linked guide.