diff --git a/.github/workflows/cubic-devin-review.yml b/.github/workflows/cubic-devin-review.yml index 11299ab300..015984f58a 100644 --- a/.github/workflows/cubic-devin-review.yml +++ b/.github/workflows/cubic-devin-review.yml @@ -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: @@ -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" \ @@ -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 @@ -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 });