I am currently setting up an automatic versioning system where if I use git commit
with the message format PATCH: {message}
, the app's patch version will automatically be updated (and the same for the prefixes MINOR
and MAJOR
as well). My approach involves using Husky for my pre-commit
and pre-push
hooks. To achieve this, I have created a .husky/commit-msg
hook that looks like this:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run auto-version $1 && git add --all
This setup works as expected, with my auto-version.js
script correctly reading the commit message and updating the ./version.json
file accordingly. However, there is one issue - the commit is generated with the old version.json
file and I'm unsure of the reason behind this. Although the git add
command seems to work fine as the updated version.json
file appears in the 'Staged Changes' section after the commit. On the other hand, my .husky/pre-commit
hook functions properly and stages the updated files before committing:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint && npm run format && git add --all
I suspect that the problem might be related to the timing between when the commit-msg
hook triggers and when git
accepts newly staged files but Husky lacks detailed documentation on the functioning of the commit-msg
hook. Additionally, I attempted to set this up using the pre-commit
hook instead, but the new commit message does not get saved to
.git/COMMIT_EDITMSG</code at this stage (only the old commit message remains).</p>
<p>To provide more context, we are currently executing <code>npm version patch --no-git-tag-version
on the pre-commit
hook and manually adjusting the minor and major versions when necessary. The goal was to establish a more reliable and automated system, which led me to face this hurdle.
auto-version.js
const { readFileSync, writeFileSync } = require('fs');
const versionJsonPath = './version.json';
const commitMessagePath = '.git/COMMIT_EDITMSG';
const prefixOptions = ['PATCH', 'MINOR', 'MAJOR'];
const postfixMinLength = 12;
(() => {
// read commit message
const rawMessage = readFileSync(commitMessagePath, 'utf-8');
const message = rawMessage.split(/\r?\n/)[0];
console.log(`Reading commit message "${message}"...`);
// check for merge commit
if (message.startsWith('Merge branch')) {
process.exit();
}
// check for core composition
const messageParts = message.split(':');
if (messageParts.length != 2) {
throwError(`Commit message should take the form "{${prefixOptions.join('|')}}: {message}".`);
}
// check for valid prefix
const messagePrefix = messageParts[0];
if (!prefixOptions.includes(messagePrefix)) {
throwError(`The commit message prefix must be one of the following version types: [${prefixOptions.join(', ')}].`);
}
// check for valid postfix
const messagePostfix = messageParts[1];
if (messagePostfix.trim().length < postfixMinLength) {
throwError(`The commit message postfix must consist of at least ${postfixMinLength} characters.`);
}
// update app version
const versionJson = JSON.parse(readFileSync(versionJsonPath, 'utf-8'));
const oldVersion = versionJson.appVersion;
const versionParts = oldVersion.split('.').map(v => parseInt(v, 10));
if (messagePrefix == 'MAJOR') {
versionParts[0]++;
versionParts[1] = 0;
versionParts[2] = 0;
} else if (messagePrefix == 'MINOR') {
versionParts[1]++;
versionParts[2] = 0;
} else {
versionParts[2]++;
}
const newVersion = versionParts.join('.');
versionJson.appVersion = newVersion;
console.log(`Updating the app version from ${oldVersion} to ${newVersion}...`);
writeFileSync(versionJsonPath, JSON.stringify(versionJson));
process.exit();
})();
function throwError(message) {
console.error(message);
process.exit(1);
}
version.json
{
"appVersion": "0.12.15"
}