UNPKG

@deriv-com/shiftai-cli

Version:

A comprehensive AI code detection and analysis CLI tool for tracking AI-generated code in projects

244 lines (203 loc) • 7.67 kB
const { spawn } = require('child_process'); const chalk = require('chalk'); const fs = require('fs-extra'); const AIStorage = require('../core/ai-storage'); const PRManager = require('../core/pr-manager'); const { getPRTargetInfo, getCurrentBranch, getCurrentCommitHash, getGitAuthor } = require('../utils/git-utils'); /** * Enhanced git push command with automatic PR creation */ async function pushCommand(args, options = {}) { try { console.log(chalk.blue('šŸš€ ShiftAI Enhanced Push')); // Get current branch before push const branchName = getCurrentBranch(); console.log(chalk.gray(`šŸ“” Pushing branch: ${branchName}`)); // Check if automatic PR creation is enabled const configPath = AIStorage.getConfigPath(); const config = fs.pathExistsSync(configPath) ? require(configPath) : {}; const createPR = config.upload?.createPR ?? true; if (createPR) { console.log(chalk.blue('āš™ļø Automatic PR creation enabled')); } else { console.log(chalk.gray('āš™ļø Automatic PR creation disabled')); } // Execute git push and monitor its completion const pushSuccess = await executeGitPush(args); if (!pushSuccess) { console.log(chalk.red('āŒ Git push failed - skipping PR creation')); process.exit(1); } console.log(chalk.green('āœ… Push completed successfully')); // If PR creation is enabled, trigger it now if (createPR) { await handlePostPushPRCreation(branchName); } else { console.log(chalk.yellow('šŸ’” Run "shai pr" to analyze and create PR manually')); } } catch (error) { console.error(chalk.red(`āŒ Push command failed: ${error.message}`)); process.exit(1); } } /** * Execute git push and monitor completion */ function executeGitPush(args) { return new Promise((resolve, reject) => { console.log(chalk.blue('šŸ“¤ Running git push...')); // Build git push command const gitArgs = ['push', ...args]; console.log(chalk.gray(` Command: git ${gitArgs.join(' ')}`)); // Spawn git push process const gitProcess = spawn('git', gitArgs, { stdio: ['inherit', 'pipe', 'pipe'], cwd: process.cwd() }); let stdout = ''; let stderr = ''; // Capture output gitProcess.stdout.on('data', (data) => { const output = data.toString(); stdout += output; // Show real-time output process.stdout.write(output); }); gitProcess.stderr.on('data', (data) => { const output = data.toString(); stderr += output; // Show real-time output process.stderr.write(output); }); gitProcess.on('close', (code) => { if (code === 0) { // Check for successful push indicators const output = stdout + stderr; const successIndicators = [ 'Total ', 'To https://github.com/', 'To git@github.com:', '-> ', 'Everything up-to-date' ]; const hasSuccessIndicator = successIndicators.some(indicator => output.includes(indicator) ); if (hasSuccessIndicator || code === 0) { resolve(true); } else { resolve(false); } } else { resolve(false); } }); gitProcess.on('error', (error) => { reject(error); }); }); } /** * Handle PR creation after successful push */ async function handlePostPushPRCreation(branchName) { try { console.log(chalk.blue('\nšŸš€ Starting automatic PR creation...')); // Load .env file if it exists try { const path = require('path'); const envPath = path.join(process.cwd(), '.env'); if (fs.pathExistsSync(envPath)) { require('dotenv').config({ path: envPath }); } } catch (error) { // Continue without .env } // Get GitHub token const githubToken = process.env.GITHUB_TOKEN; if (!githubToken) { console.log(chalk.yellow('āš ļø GITHUB_TOKEN not found - skipping PR creation')); console.log(chalk.gray(' Set GITHUB_TOKEN environment variable or add to .env file')); return; } // Get PR target info const prTargetInfo = getPRTargetInfo(); // Wait a moment for GitHub to sync the push console.log(chalk.blue('ā³ Waiting for GitHub to sync...')); await new Promise(resolve => setTimeout(resolve, 3000)); // Step 1: Initialize and analyze console.log(chalk.blue('šŸ“Š Analyzing AI content in your changes...')); const aiStorage = new AIStorage({ organization: prTargetInfo.organization, repository: prTargetInfo.repository, projectRoot: process.cwd() }); await aiStorage.initialize(); const currentCommit = getCurrentCommitHash(); const metadata = { branch: branchName, commit: currentCommit, author: getGitAuthor(), timestamp: new Date().toISOString() }; const aiData = await aiStorage.analyzeBranchChanges(metadata); // Step 2: Setup PR management const prManager = new PRManager({ organization: prTargetInfo.organization, repository: prTargetInfo.repository, token: githubToken }); // Step 3: Check for existing PR console.log(chalk.blue('šŸ” Checking for existing PR...')); let pr = await prManager.findPR(branchName, prTargetInfo); if (!pr) { // Step 4: Create new PR console.log(chalk.blue('šŸ“ Creating pull request...')); if (prTargetInfo.isCrossRepo) { console.log(chalk.gray(` Cross-repo: ${prTargetInfo.forkOwner} → ${prTargetInfo.organization}`)); } pr = await prManager.createPR(branchName, 'master', prTargetInfo); if (!pr) { console.error(chalk.red('āŒ Failed to create PR')); return; } console.log(chalk.green(`āœ… Created PR #${pr.number}: "${pr.title}"`)); } else { console.log(chalk.yellow(`šŸ“‹ Found existing PR #${pr.number}: "${pr.title}"`)); } // Step 5: Get PR URL const prUrl = prManager.getPRUrl(pr.number); console.log(chalk.cyan(`šŸ”— ${prUrl}`)); // Step 6: Post analysis comment console.log(chalk.blue('šŸ’¬ Adding AI analysis comment...')); const commentResult = await prManager.postAnalysisComment(pr.number, aiData); if (commentResult) { if (commentResult.updated) { console.log(chalk.green('āœ… Updated AI analysis comment')); } else { console.log(chalk.green('āœ… Posted AI analysis comment')); } } else { console.log(chalk.yellow('āš ļø Comment posting failed - check token permissions')); } // Step 7: Auto-open PR in browser console.log(chalk.blue('🌐 Opening PR in browser...')); try { const { execSync } = require('child_process'); const platform = process.platform; let openCommand; if (platform === 'darwin') openCommand = 'open'; else if (platform === 'win32') openCommand = 'start'; else openCommand = 'xdg-open'; execSync(`${openCommand} "${prUrl}"`, { stdio: 'ignore' }); console.log(chalk.green('āœ… Opened in browser')); } catch (error) { console.log(chalk.gray('šŸ’” Copy the URL above to open manually')); } console.log(chalk.green('šŸŽ‰ PR creation completed successfully!')); } catch (error) { console.error(chalk.red(`āŒ PR creation failed: ${error.message}`)); console.log(chalk.yellow('šŸ’” Run "shai pr" manually to create the PR')); } } module.exports = pushCommand;