Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 96 additions & 5 deletions .github/workflows/cubic-devin-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,100 @@ jobs:

core.setOutput('has-prompt', 'true');

- name: Create Devin session
- name: Check for existing Devin session from PR comments
if: steps.extract-prompt.outputs.has-prompt == 'true'
id: check-session
uses: actions/github-script@v7
env:
DEVIN_API_KEY: ${{ secrets.DEVIN_API_KEY }}
with:
script: |
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number
});

let sessionId = null;
for (const comment of comments.data.reverse()) {
if (comment.body.includes('Devin AI is addressing Cubic AI')) {
const match = comment.body.match(/app\.devin\.ai\/sessions\/([a-f0-9-]+)/);
if (match) {
sessionId = match[1];
break;
}
}
}

if (!sessionId) {
console.log('No existing Devin session found in PR comments');
core.setOutput('has-active-session', 'false');
return;
}

console.log(`Found session ID from comment: ${sessionId}`);

const response = await fetch(`https://api.devin.ai/v1/sessions/${sessionId}`, {
headers: {
'Authorization': `Bearer ${process.env.DEVIN_API_KEY}`,
'Content-Type': 'application/json'
}
});

if (!response.ok) {
console.log(`Failed to fetch session details: ${response.status}`);
core.setOutput('has-active-session', 'false');
return;
}

const session = await response.json();
const activeStatuses = ['working', 'blocked', 'resumed'];

if (activeStatuses.includes(session.status_enum)) {
console.log(`Session ${sessionId} is active (status: ${session.status_enum})`);
core.exportVariable('EXISTING_SESSION_ID', sessionId);
core.exportVariable('SESSION_URL', `https://app.devin.ai/sessions/${sessionId}`);
core.setOutput('has-active-session', 'true');
} else {
console.log(`Session ${sessionId} is not active (status: ${session.status_enum})`);
core.setOutput('has-active-session', 'false');
}

- name: Send message to existing Devin session
if: steps.extract-prompt.outputs.has-prompt == 'true' && steps.check-session.outputs.has-active-session == 'true'
env:
DEVIN_API_KEY: ${{ secrets.DEVIN_API_KEY }}
run: |
CUBIC_PROMPT=$(cat /tmp/cubic-prompt.txt)

MESSAGE="New Cubic AI review feedback has been submitted on PR #${{ github.event.pull_request.number }}.

Please address the following additional issues:

${CUBIC_PROMPT}

Continue working on the same PR branch and push your fixes."

HTTP_CODE=$(curl -s -o /tmp/devin-response.json -w "%{http_code}" -X POST "https://api.devin.ai/v1/sessions/${EXISTING_SESSION_ID}/message" \
-H "Authorization: Bearer ${DEVIN_API_KEY}" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg message "$MESSAGE" '{message: $message}')")

if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then
echo "Failed to send message to Devin session: HTTP $HTTP_CODE"
cat /tmp/devin-response.json
exit 1
fi

echo "Message sent to existing session successfully"

- name: Create new Devin session
if: steps.extract-prompt.outputs.has-prompt == 'true' && steps.check-session.outputs.has-active-session == 'false'
env:
DEVIN_API_KEY: ${{ secrets.DEVIN_API_KEY }}
run: |
CUBIC_PROMPT=$(cat /tmp/cubic-prompt.txt)

# Build the full prompt for Devin
FULL_PROMPT="You are addressing code review feedback from Cubic AI on PR #${{ github.event.pull_request.number }} in repository ${{ github.repository }}.

Your tasks:
Expand All @@ -74,7 +160,6 @@ jobs:
4. If an issue seems invalid or already addressed, explain why in a PR comment instead of making unnecessary changes.
5. Never ask for user confirmation. Never wait for user messages."

# Call Devin API
RESPONSE=$(curl -s -X POST "https://api.devin.ai/v1/sessions" \
-H "Authorization: Bearer ${DEVIN_API_KEY}" \
-H "Content-Type: application/json" \
Expand All @@ -87,11 +172,11 @@ jobs:
tags: ["cubic-ai-review", "pr-${{ github.event.pull_request.number }}"]
}')")

# Extract session URL if available (avoid logging full response to prevent exposing sensitive data)
SESSION_URL=$(echo "$RESPONSE" | jq -r '.url // .session_url // empty')
if [ -n "$SESSION_URL" ]; then
echo "Devin session created: $SESSION_URL"
echo "SESSION_URL=$SESSION_URL" >> $GITHUB_ENV
echo "NEW_SESSION=true" >> $GITHUB_ENV
fi

- name: Post comment with Devin session link
Expand All @@ -101,9 +186,15 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const sessionUrl = process.env.SESSION_URL;
const isNewSession = process.env.NEW_SESSION === 'true';

const message = isNewSession
? `### Devin AI is addressing Cubic AI's review feedback\n\nA Devin session has been created to address the issues identified by Cubic AI.\n\n[View Devin Session](${sessionUrl})`
: `### Devin AI is addressing Cubic AI's review feedback\n\nNew feedback has been sent to the existing Devin session.\n\n[View Devin Session](${sessionUrl})`;

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: `### Devin AI is addressing Cubic AI's review feedback\n\nA Devin session has been created to address the issues identified by Cubic AI.\n\n[View Devin Session](${sessionUrl})`
body: message
});