You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
5.0 KiB
153 lines
5.0 KiB
// Note: Using spawn instead of execSync for security (prevents command injection) |
|
|
|
/** |
|
* Push to all remotes |
|
* Security: Uses spawn with argument arrays to prevent command injection |
|
*/ |
|
export async function pushAll(args, server, json) { |
|
// Check for help flag |
|
if (args.includes('--help') || args.includes('-h')) { |
|
console.log(`Push to All Remotes |
|
|
|
Usage: gitrep push-all [branch] [options] |
|
|
|
Description: |
|
Pushes the current branch (or specified branch) to all configured git remotes. |
|
This is useful when you have multiple remotes (e.g., GitHub, GitLab, GitRepublic) |
|
and want to push to all of them at once. |
|
|
|
Arguments: |
|
branch Optional branch name to push. If not specified, pushes all branches. |
|
|
|
Options: |
|
--force, -f Force push (use with caution) |
|
--tags Also push tags |
|
--dry-run, -n Show what would be pushed without actually pushing |
|
--help, -h Show this help message |
|
|
|
Examples: |
|
gitrep push-all Push all branches to all remotes |
|
gitrep push-all main Push main branch to all remotes |
|
gitrep push-all main --force Force push main branch to all remotes |
|
gitrep push-all --tags Push all branches and tags to all remotes |
|
gitrep push-all main --dry-run Show what would be pushed without pushing |
|
|
|
Notes: |
|
- This command requires you to be in a git repository |
|
- It will push to all remotes listed by 'git remote' |
|
- If any remote fails, the command will exit with an error code |
|
- Use --dry-run to test before actually pushing |
|
`); |
|
return; |
|
} |
|
|
|
// Parse arguments |
|
const branch = args.find(arg => !arg.startsWith('--')); |
|
const force = args.includes('--force') || args.includes('-f'); |
|
const tags = args.includes('--tags'); |
|
const dryRun = args.includes('--dry-run') || args.includes('-n'); |
|
|
|
// Get all remotes |
|
// Security: Use spawn with argument arrays to prevent command injection |
|
let remotes = []; |
|
try { |
|
const { spawn } = await import('child_process'); |
|
const remoteOutput = await new Promise((resolve, reject) => { |
|
const proc = spawn('git', ['remote'], { encoding: 'utf-8' }); |
|
let output = ''; |
|
proc.stdout.on('data', (chunk) => { output += chunk.toString(); }); |
|
proc.on('close', (code) => { |
|
if (code === 0) resolve(output.trim()); |
|
else reject(new Error(`git remote exited with code ${code}`)); |
|
}); |
|
proc.on('error', reject); |
|
}); |
|
remotes = remoteOutput.split('\n').filter(r => r.trim()); |
|
} catch (err) { |
|
console.error('Error: Not in a git repository or unable to read remotes'); |
|
console.error(err instanceof Error ? err.message : 'Unknown error'); |
|
process.exit(1); |
|
} |
|
|
|
if (remotes.length === 0) { |
|
console.error('Error: No remotes configured'); |
|
process.exit(1); |
|
} |
|
|
|
// Build push command |
|
const pushArgs = []; |
|
if (force) pushArgs.push('--force'); |
|
if (tags) pushArgs.push('--tags'); |
|
if (dryRun) pushArgs.push('--dry-run'); |
|
if (branch) { |
|
// If branch is specified, push to each remote with that branch |
|
pushArgs.push(branch); |
|
} else { |
|
// Push all branches |
|
pushArgs.push('--all'); |
|
} |
|
|
|
const results = []; |
|
let successCount = 0; |
|
let failCount = 0; |
|
|
|
for (const remote of remotes) { |
|
try { |
|
if (!json && !dryRun) { |
|
console.log(`\nPushing to ${remote}...`); |
|
} |
|
|
|
// Security: Use spawn with argument arrays to prevent command injection |
|
const { spawn } = await import('child_process'); |
|
const command = ['push', remote, ...pushArgs]; |
|
|
|
await new Promise((resolve, reject) => { |
|
const proc = spawn('git', command, { |
|
stdio: json ? 'pipe' : 'inherit', |
|
encoding: 'utf-8' |
|
}); |
|
proc.on('close', (code) => { |
|
if (code === 0) resolve(); |
|
else reject(new Error(`git push exited with code ${code}`)); |
|
}); |
|
proc.on('error', reject); |
|
}); |
|
|
|
results.push({ remote, status: 'success' }); |
|
successCount++; |
|
|
|
if (!json && !dryRun) { |
|
console.log(`✅ Successfully pushed to ${remote}`); |
|
} |
|
} catch (err) { |
|
const errorMessage = err instanceof Error ? err.message : 'Unknown error'; |
|
results.push({ remote, status: 'failed', error: errorMessage }); |
|
failCount++; |
|
|
|
if (!json && !dryRun) { |
|
console.error(`❌ Failed to push to ${remote}: ${errorMessage}`); |
|
} |
|
} |
|
} |
|
|
|
if (json) { |
|
console.log(JSON.stringify({ |
|
total: remotes.length, |
|
success: successCount, |
|
failed: failCount, |
|
results |
|
}, null, 2)); |
|
} else { |
|
console.log('\n' + '='.repeat(70)); |
|
console.log(`Push Summary: ${successCount} succeeded, ${failCount} failed out of ${remotes.length} remotes`); |
|
console.log('='.repeat(70)); |
|
|
|
if (failCount > 0) { |
|
console.log('\nFailed remotes:'); |
|
results.filter(r => r.status === 'failed').forEach(r => { |
|
console.log(` ${r.remote}: ${r.error}`); |
|
}); |
|
process.exit(1); |
|
} |
|
} |
|
}
|
|
|