Devin’s API is a powerful tool, please read the following article before continuing with this guide.
1. Process Overview
-
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.
-
GitLab Action is triggered: The opening of the PR automatically triggers a GitHub Action workflow.
-
GitLab Action calls Devin API: The GitHub Action sends a request to the Devin API, passing the identified issues for automated resolution.
-
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.
-
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
- Configure GitLab environment to hold the required secrets:
- Set up the GitLab 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.
- Test the Integration
Once your setup is complete, you can test the integration by manually triggering a GitLab Action. This will allow you to verify that the action correctly calls the Devin API and resolves the identified issues.
- View Devin Sessions Page
After the GitLab Build 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.
As mentioned before the required Values from SonarQube are:
To configure the integration, you will need to obtain the following three values from your
SonarQube instance:
You will need three_values from SonarQube: {SONAR_TOKEN, SONAR_ORG, SONAR_PROJECT_KEY}
Once you have all the required values, you’re ready to configure the GitLab 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 GitLab Action has the following source code
stages:
- analyze
- remediate
variables:
SONAR_TOKEN: "$SONAR_TOKEN"
SONAR_ORG: "$SONAR_ORG"
SONAR_PROJECT_KEY: "Colhodm_juice-shop"
DEVIN_API_KEY: "$DEVIN_API_KEY"
GIT_STRATEGY: clone
analyze:
stage: analyze
image: sonarsource/sonar-scanner-cli:latest
script:
- sonar-scanner -Dsonar.organization=$SONAR_ORG
only:
- branches
- merge_requests
setup_python:
stage: remediate
image: python:3.9
before_script:
- python --version
- pip install aiohttp
script:
- git config --global user.name "GitLab Runner"
- git config --global user.email "runner@gitlab.com"
- python3 $CI_PROJECT_DIR/.gitlab/scripts/devin_remediation.py
only:
- branches
- merge_requests
As a reminder devin_remediation.py is the following:
python
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 GitLab Action sets the correct environment variables, add them to GitLab CI/CD Secrets.
Navigating to the correct settings can be tricky. Go to Settings and edit Secrets. Add SONAR_TOKEN and DEVINS_API under Repository Secrets.
If you use self-hosted GitLab, then the only difference would be:
Once configured, you can monitor your GitLab 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.