chore(package): add auto-version script
This commit is contained in:
185
scripts/auto-version.ts
Normal file
185
scripts/auto-version.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user