chore(package): add auto-version script
This commit is contained in:
32
.husky/post-commit
Executable file
32
.husky/post-commit
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Auto-version hook: incrémente la version après certains commits
|
||||||
|
|
||||||
|
# Récupérer le dernier message de commit
|
||||||
|
commit_msg=$(git log -1 --pretty=%B)
|
||||||
|
|
||||||
|
# Ignorer si le commit contient [skip version] ou si c'est un commit de version
|
||||||
|
if echo "$commit_msg" | grep -qE "\[skip version\]|chore: bump version"; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Vérifier si le commit devrait déclencher une mise à jour de version
|
||||||
|
# Types pris en charge:
|
||||||
|
# - feat: → minor bump
|
||||||
|
# - fix:, perf:, security:, patch:, refactor: → patch bump
|
||||||
|
# - feat!:, refactor!:, etc. (avec !) → major bump
|
||||||
|
# - breaking change ou BREAKING CHANGE → major bump
|
||||||
|
# Ignorés: chore:, docs:, style:, test:, ci:, build:
|
||||||
|
if echo "$commit_msg" | grep -qiE "^feat:|^fix:|^perf:|^security:|^patch:|^refactor:|^[a-z]+!:|breaking change"; then
|
||||||
|
# Lancer le script en mode hook (silent + ajout auto au staging)
|
||||||
|
pnpm tsx scripts/auto-version.ts --silent --hook
|
||||||
|
|
||||||
|
# Vérifier si package.json a changé
|
||||||
|
if ! git diff --quiet package.json; then
|
||||||
|
echo ""
|
||||||
|
echo "📦 Version mise à jour automatiquement"
|
||||||
|
echo "💡 Pour commit: git add package.json && git commit -m 'chore: bump version'"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
||||||
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