1 line
13 KiB
JSON
1 line
13 KiB
JSON
{"updatedAt":"2025-12-02T17:29:22.862Z","createdAt":"2025-12-02T17:28:18.023Z","id":"fy6ooiuhTyQrOhlv","name":"Phase 3: TASK.md Driven with Retry (FIXED)","description":null,"active":true,"isArchived":false,"nodes":[{"parameters":{"httpMethod":"POST","path":"openhands-build-test","options":{}},"id":"webhook","name":"Gitea Webhook","type":"n8n-nodes-base.webhook","typeVersion":1.1,"position":[240,300],"webhookId":"openhands-build-test"},{"parameters":{"jsCode":"const payload = $json.body || $json;\n\nconst repoName = payload.repository?.name || 'vue-ai-agency';\nconst repoFullName = payload.repository?.full_name || 'gitadmin/vue-ai-agency';\nconst branch = payload.ref?.replace('refs/heads/', '') || 'main';\nconst commitSha = payload.after || '';\nconst commitMsg = payload.commits?.[0]?.message || '';\n\n// SKIP: Detect OpenHands commits to prevent infinite loop\nif (commitMsg.toLowerCase().includes('openhands')) {\n return {\n skip: true,\n reason: 'OpenHands commit detected - skipping',\n repo_name: repoName,\n commit_message: commitMsg\n };\n}\n\nconst projectDir = '/home/bam/claude/' + repoName;\n\nreturn {\n skip: false,\n repo_name: repoName,\n repo_full_name: repoFullName,\n branch: branch,\n commit_sha: commitSha,\n commit_message: commitMsg,\n project_dir: projectDir,\n timestamp: new Date().toISOString()\n};"},"id":"extract-info","name":"Extract Info","type":"n8n-nodes-base.code","typeVersion":2,"position":[460,300]},{"parameters":{"command":"cat /home/bam/.gitea_api_token","sessionId":"get-token","authentication":"privateKey","options":{}},"id":"get-token","name":"Get Token","type":"n8n-nodes-base.ssh","typeVersion":1,"position":[460,500],"credentials":{"sshPrivateKey":{"id":"v2BMXeCFGpXaoIyb","name":"SSH Private Key account"}}},{"parameters":{"conditions":{"boolean":[{"value1":"={{ $json.skip }}","value2":true}]}},"id":"check-skip","name":"Should Skip?","type":"n8n-nodes-base.if","typeVersion":1,"position":[680,300]},{"parameters":{"command":"={{ 'cat ' + $json.project_dir + '/TASK.md 2>/dev/null || echo \"No TASK.md found\"' }}","sessionId":"read-task","authentication":"privateKey","options":{}},"id":"read-task-file","name":"Read TASK.md","type":"n8n-nodes-base.ssh","typeVersion":1,"position":[900,300],"credentials":{"sshPrivateKey":{"id":"v2BMXeCFGpXaoIyb","name":"SSH Private Key account"}}},{"parameters":{"jsCode":"// Get retry counter from workflow static data\nconst staticData = $getWorkflowStaticData('global');\nstaticData.retry_count = (staticData.retry_count || 0) + 1;\n\nconst retryCount = staticData.retry_count;\n\n// FAIL: Max retries exceeded\nif (retryCount >= 3) {\n return {\n action: 'FAIL',\n status: 'FAILED',\n retry_count: retryCount,\n max_retries: 3,\n message: `❌ Build failed after 3 attempts`,\n repo: $json.repo_full_name,\n commit_sha: $json.commit_sha\n };\n}\n\nconst sshOutput = $json;\nconst repoData = $node['Extract Info'].json;\nconst tokenData = $node['Get Token'].json;\n\nconst taskContent = sshOutput.stdout?.trim() || '';\nconst hasTask = taskContent && !taskContent.includes('No TASK.md found');\n\nif (!hasTask) {\n return {\n status: 'SKIPPED',\n message: 'No TASK.md found',\n retry_count: retryCount\n };\n}\n\n// Build task message\nlet taskMsg = '';\nconst prevResult = $input.first()?.json?.build_result;\n\nif (prevResult && prevResult.status === 'FAILED') {\n // Retry with error feedback\n taskMsg = `🔄 BUILD RETRY - Attempt ${retryCount}/3\\n\\n` +\n `Previous build FAILED with errors:\\n` +\n `${prevResult.errors || 'Unknown error'}\\n\\n` +\n `Please review the task and fix these issues:\\n\\n` +\n `Working in directory ${repoData.project_dir}:\\n\\n` +\n `Read and implement the following task from TASK.md:\\n\\n` +\n `${taskContent}\\n\\n` +\n `After completing the task, rename TASK.md to TASK_DONE.md`;\n} else {\n // Initial build\n taskMsg = `Working in directory ${repoData.project_dir}:\\n\\n` +\n `Read and implement the following task from TASK.md:\\n\\n` +\n `${taskContent}\\n\\n` +\n `After completing the task, rename TASK.md to TASK_DONE.md`;\n}\n\nreturn {\n action: 'RETRY',\n status: 'IN_PROGRESS',\n retry_count: retryCount,\n max_retries: 3,\n task: taskMsg,\n repo: repoData.repo_full_name,\n commit_sha: repoData.commit_sha,\n project_dir: repoData.project_dir,\n gitea_token: tokenData.stdout || tokenData\n};"},"id":"prepare-task","name":"Prepare Task with Retry","type":"n8n-nodes-base.code","typeVersion":2,"position":[1120,300]},{"parameters":{"command":"={{ 'sh /home/bam/openhands-sdk-wrapper-sh.sh \"' + $json.task.replace(/\"/g, '\\\\\"') + '\" \"' + $json.project_dir + '\"' }}","sessionId":"openhands-task","authentication":"privateKey","options":{}},"id":"run-openhands","name":"Run OpenHands","type":"n8n-nodes-base.ssh","typeVersion":1,"position":[1340,300],"credentials":{"sshPrivateKey":{"id":"v2BMXeCFGpXaoIyb","name":"SSH Private Key account"}}},{"parameters":{"jsCode":"const sshOutput = $json;\nconst buildData = $node['Prepare Task with Retry'].json;\n\nconst stdout = sshOutput.stdout || '';\nconst stderr = sshOutput.stderr || '';\nconst exitCode = sshOutput.code || 0;\n\n// DETERMINE BUILD STATUS\nlet status = 'FAILED';\nlet errors = '';\n\n// Check for common success patterns\nconst hasTestPassing = stdout.includes('passing') || stdout.includes('✓') || stdout.includes('PASS');\nconst hasSuccessKeyword = stdout.includes('success') || stdout.includes('build complete');\nconst hasNoErrors = !stderr || (stderr.length < 50 && !stderr.toLowerCase().includes('error'));\n\n// Check for failure patterns\nconst hasFailingTests = stdout.includes('failing') || stdout.includes('✗') || stdout.includes('FAIL');\nconst hasErrors = stderr.toLowerCase().includes('error') || stderr.toLowerCase().includes('fail');\n\n// Determine status\nif (exitCode === 0 && (hasTestPassing || hasSuccessKeyword || hasNoErrors)) {\n status = 'SUCCESS';\n} else if (hasFailingTests || hasErrors || exitCode !== 0) {\n status = 'FAILED';\n errors = stderr || stdout || `Process exited with code ${exitCode}`;\n}\n\nreturn {\n ...buildData,\n build_result: {\n status: status,\n exit_code: exitCode,\n stdout: stdout.substring(0, 2000),\n stderr: stderr.substring(0, 2000),\n errors: errors.substring(0, 1000)\n }\n};"},"id":"analyze-result","name":"Analyze Result","type":"n8n-nodes-base.code","typeVersion":2,"position":[1560,300]},{"parameters":{"conditions":{"string":[{"value1":"={{ $json.build_result.status }}","operation":"equal","value2":"SUCCESS"}]}},"id":"success-check","name":"Build Success?","type":"n8n-nodes-base.if","typeVersion":1,"position":[1780,300]},{"parameters":{"command":"={{ 'cd ' + $json.project_dir + ' && git add -A && git commit -m \"OpenHands: Build successful\" && git push origin main 2>&1 || echo \"Nothing to commit\"' }}","sessionId":"git-push","authentication":"privateKey","options":{}},"id":"git-commit-push","name":"Git Commit & Push","type":"n8n-nodes-base.ssh","typeVersion":1,"position":[2000,200],"credentials":{"sshPrivateKey":{"id":"v2BMXeCFGpXaoIyb","name":"SSH Private Key account"}}},{"parameters":{"jsCode":"const buildResult = $json.build_result;\nconst buildData = $node['Prepare Task with Retry'].json;\n\n// SUCCESS RESPONSE\nconst successMessage = `✅ BUILD SUCCESSFUL\\n\\n` +\n `Repository: ${buildData.repo}\\n` +\n `Commit: ${buildData.commit_sha}\\n` +\n `Attempt: ${buildData.retry_count}/3\\n\\n` +\n `Result: Build completed successfully!\\n\\n` +\n `OpenHands has successfully built and tested the project.\\n` +\n `The commit status has been updated in Gitea.`;\n\n// Reset retry counter on success\nconst staticData = $getWorkflowStaticData('global');\nstaticData.retry_count = 0;\n\nreturn {\n status: 'SUCCESS',\n action: 'COMPLETED',\n message: successMessage,\n repo: buildData.repo,\n commit_sha: buildData.commit_sha,\n retry_count: buildData.retry_count,\n build_result: buildResult\n};"},"id":"handle-success","name":"Handle Success","type":"n8n-nodes-base.code","typeVersion":2,"position":[2220,200]},{"parameters":{"command":"={{ 'curl -X POST \"https://git.oky.sh/api/v1/repos/' + $json.repo.split('/')[0] + '/' + $json.repo.split('/')[1] + '/statuses/' + $json.commit_sha + '\" -H \"Authorization: token ' + $json.gitea_token + '\" -H \"Content-Type: application/json\" -d \"{\\\"state\\\": \\\"success\\\", \\\"description\\\": \\\"Build successful after \" + $json.retry_count + \" attempt(s)\\\", \\\"context\\\": \\\"openhands-ci\\\"}\" -s' }}","sessionId":"update-gitea-success","authentication":"privateKey","options":{}},"id":"update-gitea-success","name":"Update Gitea Success","type":"n8n-nodes-base.ssh","typeVersion":1,"position":[2440,200],"credentials":{"sshPrivateKey":{"id":"v2BMXeCFGpXaoIyb","name":"SSH Private Key account"}}},{"parameters":{"jsCode":"const buildResult = $json.build_result;\nconst buildData = $node['Prepare Task with Retry'].json;\n\n// FAILURE RESPONSE\nconst remaining = buildData.max_retries - buildData.retry_count;\nconst willRetry = remaining > 0;\n\nconst failureMessage = `❌ BUILD FAILED (Attempt ${buildData.retry_count}/${buildData.max_retries})\\n\\n` +\n `Repository: ${buildData.repo}\\n` +\n `Commit: ${buildData.commit_sha}\\n\\n` +\n `Errors:\\n${buildResult.errors}\\n\\n` +\n (willRetry\n ? `⏳ Will retry (${remaining} attempts remaining)`\n : `⚠️ MAX RETRIES REACHED - Build failed permanently`);\n\nreturn {\n status: 'FAILED',\n action: willRetry ? 'RETRY' : 'GIVE_UP',\n message: failureMessage,\n repo: buildData.repo,\n commit_sha: buildData.commit_sha,\n retry_count: buildData.retry_count,\n remaining: remaining,\n build_result: buildResult,\n will_retry: willRetry\n};"},"id":"handle-failure","name":"Handle Failure","type":"n8n-nodes-base.code","typeVersion":2,"position":[2000,400]},{"parameters":{"respondWith":"json","responseBody":"={{ $json }}","options":{}},"id":"respond","name":"Respond","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[2660,300]},{"parameters":{"jsCode":"return {\n status: 'SKIPPED',\n message: 'OpenHands commit - skipped to prevent loop',\n timestamp: new Date().toISOString()\n};"},"id":"skip-message","name":"Skip Message","type":"n8n-nodes-base.code","typeVersion":2,"position":[900,500]}],"connections":{"Gitea Webhook":{"main":[[{"node":"Extract Info","type":"main","index":0}]]},"Extract Info":{"main":[[{"node":"Get Token","type":"main","index":0}]]},"Get Token":{"main":[[{"node":"Should Skip?","type":"main","index":0}]]},"Should Skip?":{"main":[[{"node":"Skip Message","type":"main","index":0}],[{"node":"Read TASK.md","type":"main","index":0}]]},"Read TASK.md":{"main":[[{"node":"Prepare Task with Retry","type":"main","index":0}]]},"Prepare Task with Retry":{"main":[[{"node":"Run OpenHands","type":"main","index":0}]]},"Run OpenHands":{"main":[[{"node":"Analyze Result","type":"main","index":0}]]},"Analyze Result":{"main":[[{"node":"Build Success?","type":"main","index":0}]]},"Build Success?":{"main":[[{"node":"Git Commit & Push","type":"main","index":0}],[{"node":"Handle Failure","type":"main","index":0}]]},"Git Commit & Push":{"main":[[{"node":"Handle Success","type":"main","index":0}]]},"Handle Success":{"main":[[{"node":"Update Gitea Success","type":"main","index":0}]]},"Update Gitea Success":{"main":[[{"node":"Respond","type":"main","index":0}]]},"Handle Failure":{"main":[[{"node":"Respond","type":"main","index":0}],[{"node":"Prepare Task with Retry","type":"main","index":0}]]},"Skip Message":{"main":[[{"node":"Respond","type":"main","index":0}]]}},"settings":{"executionOrder":"v1","callerPolicy":"workflowsFromSameOwner","availableInMCP":false},"staticData":null,"meta":null,"pinData":null,"versionId":"5c85a30d-72df-4aaf-8849-d186ad6b59e5","versionCounter":3,"triggerCount":1,"shared":[{"updatedAt":"2025-12-02T17:28:18.025Z","createdAt":"2025-12-02T17:28:18.025Z","role":"workflow:owner","workflowId":"fy6ooiuhTyQrOhlv","projectId":"18Ie3sGopJUKowvQ","project":{"updatedAt":"2025-11-28T21:55:42.833Z","createdAt":"2025-11-28T21:54:40.915Z","id":"18Ie3sGopJUKowvQ","name":"pi raj <aidev@oky.sh>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-11-28T21:54:40.915Z","createdAt":"2025-11-28T21:54:40.915Z","userId":"9ac6189d-d9fb-457e-893e-27b9af3fa738","projectId":"18Ie3sGopJUKowvQ","user":{"updatedAt":"2025-12-02T07:18:21.000Z","createdAt":"2025-11-28T21:54:40.486Z","id":"9ac6189d-d9fb-457e-893e-27b9af3fa738","email":"aidev@oky.sh","firstName":"pi","lastName":"raj","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-11-28T21:55:59.720Z","personalization_survey_n8n_version":"1.121.3"},"settings":{"userActivated":true,"firstSuccessfulWorkflowId":"hwbFEoEIgGyjV0He","userActivatedAt":1764591198155},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-12-01","isPending":false}}]}}],"tags":[]} |