import { execSync } from 'child_process'; import { readFileSync, writeFileSync } from 'fs'; import { join } from 'path'; interface Version { major: number; minor: number; patch: number; } function parseVersion(version: string): Version { const [major, minor, patch] = version.split('.').map(Number); return { major, minor, patch }; } function formatVersion(v: Version): string { return `${v.major}.${v.minor}.${v.patch}`; } function getLastVersionTag(): string | null { try { const tag = execSync('git describe --tags --abbrev=0', { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'], }).trim(); return tag; } catch { return null; } } function getCommitsSinceTag(tag: string | null): string[] { try { const range = tag ? `${tag}..HEAD` : 'HEAD'; const commits = execSync(`git log ${range} --pretty=format:"%s"`, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'], }) .trim() .split('\n') .filter(Boolean); return commits; } catch { return []; } } function determineVersionBump(commits: string[]): 'major' | 'minor' | 'patch' { let hasBreaking = false; let hasFeature = false; let hasPatch = false; for (const commit of commits) { const lowerCommit = commit.toLowerCase(); // Breaking changes (major bump) if ( lowerCommit.includes('breaking change') || lowerCommit.includes('breaking:') || lowerCommit.match(/^[a-z]+!:/) || // feat!:, refactor!:, etc. lowerCommit.includes('!') ) { hasBreaking = true; } // Features (minor bump) if (lowerCommit.startsWith('feat:')) { hasFeature = true; } // Patch bumps: fixes, performance improvements, security fixes, refactorings if ( lowerCommit.startsWith('fix:') || lowerCommit.startsWith('perf:') || lowerCommit.startsWith('security:') || lowerCommit.startsWith('patch:') || lowerCommit.startsWith('refactor:') ) { hasPatch = true; } } if (hasBreaking) return 'major'; if (hasFeature) return 'minor'; if (hasPatch) return 'patch'; // Par défaut, patch si on a des commits mais aucun type spécifique return commits.length > 0 ? 'patch' : 'patch'; } function incrementVersion( current: Version, type: 'major' | 'minor' | 'patch' ): Version { switch (type) { case 'major': return { major: current.major + 1, minor: 0, patch: 0 }; case 'minor': return { major: current.major, minor: current.minor + 1, patch: 0 }; case 'patch': return { major: current.major, minor: current.minor, patch: current.patch + 1, }; } } function main() { const silent = process.argv.includes('--silent'); const hookMode = process.argv.includes('--hook'); try { const packagePath = join(process.cwd(), 'package.json'); const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8')); const currentVersion = parseVersion(packageJson.version); const lastTag = getLastVersionTag(); const commits = getCommitsSinceTag(lastTag); if (commits.length === 0) { if (!silent) { console.log('✅ Aucun nouveau commit depuis la dernière version'); console.log(`Version actuelle: ${packageJson.version}`); } return; } const bumpType = determineVersionBump(commits); const newVersion = incrementVersion(currentVersion, bumpType); const newVersionString = formatVersion(newVersion); // Si la version n'a pas changé, ne rien faire if (newVersionString === packageJson.version) { return; } if (!silent) { console.log(`📊 Analyse des commits depuis ${lastTag || 'le début'}:`); console.log(` - ${commits.length} commit(s) trouvé(s)`); console.log(` - Type de bump détecté: ${bumpType}`); console.log(` - Version actuelle: ${packageJson.version}`); console.log(` - Nouvelle version: ${newVersionString}`); // Afficher les commits pertinents console.log('\n📝 Commits analysés:'); commits.slice(0, 10).forEach((commit) => { console.log(` - ${commit}`); }); if (commits.length > 10) { console.log(` ... et ${commits.length - 10} autre(s) commit(s)`); } } // Mettre à jour package.json packageJson.version = newVersionString; writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + '\n'); if (!silent) { console.log(`\n✅ Version mise à jour dans package.json`); console.log( `\n💡 Prochaines étapes:` + `\n 1. git add package.json` + `\n 2. git commit -m "chore: bump version to ${newVersionString}"` + `\n 3. git tag v${newVersionString}` ); } else if (hookMode) { // En mode hook, ajouter package.json au staging try { execSync('git add package.json', { stdio: 'ignore' }); } catch { // Ignorer les erreurs en mode hook } } } catch (error) { if (!silent) { console.error('❌ Erreur lors de la mise à jour de version:', error); } if (!hookMode) { process.exit(1); } } } main();