Files
native/scripts/bump-version.mjs
Linus Rath 746572c784 fix: raise the android versionCode with every release
`versionCode` was hard-coded to 1, so every release looked like the
same version to Android. It is now derived from `VERSION` as
`major * 10000000 + minor * 10000 + patch` (0.1.62 gives 10062), which
is above the 1 existing installs have and keeps growing, so Android
never mistakes an update for a downgrade.

The formula only increases while patch stays below 10000 and minor
below 1000. `build.gradle` refuses a `VERSION` outside that range or
outside Android's 2100000000 ceiling, and `scripts/bump-version.mjs`,
which only ever bumps patch, refuses to create such a version before
anything is committed or tagged.
2026-09-24 18:52:15 +02:00

91 lines
3.4 KiB
JavaScript

#!/usr/bin/env node
import { execFileSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const versionFile = path.join(root, 'VERSION');
const pkgFile = path.join(root, 'package.json');
const lockFile = path.join(root, 'package-lock.json');
const git = (...args) =>
execFileSync('git', args, { cwd: root, stdio: ['ignore', 'pipe', 'inherit'] }).toString().trim();
const gitInherit = (...args) =>
execFileSync('git', args, { cwd: root, stdio: 'inherit' });
// Pre-flight: the release is created with the GitHub CLI as the last step.
// Check it is installed and logged in BEFORE anything is committed, tagged or
// pushed - otherwise a missing login leaves a pushed tag with no release (and
// no release workflow run), which is exactly what a re-run cannot repair.
try {
execFileSync('gh', ['auth', 'status'], { cwd: root, stdio: 'ignore' });
} catch {
console.error('GitHub CLI is not available or not logged in.');
console.error('Run `gh auth login` (or set GH_TOKEN), then re-run `npm run bump`.');
process.exit(1);
}
const dirty = git('status', '--porcelain');
if (dirty) {
console.error('Working tree is not clean. Commit or stash changes before bumping.');
console.error(dirty);
process.exit(1);
}
const current = fs.readFileSync(versionFile, 'utf8').trim();
const parts = current.split('.').map(Number);
if (parts.length !== 3 || parts.some(Number.isNaN)) {
console.error(`Invalid version in VERSION: "${current}" (expected MAJOR.MINOR.PATCH)`);
process.exit(1);
}
parts[2] += 1;
const next = parts.join('.');
// android/app/build.gradle derives versionCode as
// major * 10000000 + minor * 10000 + patch. Past these limits it would stop
// increasing, and Android refuses an update with a lower versionCode.
if (parts[1] > 999 || parts[2] > 9999) {
console.error(`${next} would break the Android versionCode: minor must stay below 1000 and patch below 10000.`);
console.error('Raise the minor or major version in VERSION by hand and commit it, then bump again.');
process.exit(1);
}
const existingTags = new Set(git('tag', '--list').split(/\r?\n/).filter(Boolean));
if (existingTags.has(next)) {
console.error(`Tag ${next} already exists.`);
process.exit(1);
}
fs.writeFileSync(versionFile, `${next}\n`);
const pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf8'));
pkg.version = next;
fs.writeFileSync(pkgFile, `${JSON.stringify(pkg, null, 2)}\n`);
const lock = JSON.parse(fs.readFileSync(lockFile, 'utf8'));
lock.version = next;
if (lock.packages?.['']) lock.packages[''].version = next;
fs.writeFileSync(lockFile, `${JSON.stringify(lock, null, 2)}\n`);
console.log(`${current} -> ${next}`);
gitInherit('add', 'VERSION', 'package.json', 'package-lock.json');
gitInherit('commit', '-m', `chore: update version number to ${next}`);
gitInherit('tag', '-a', next, '-m', next);
gitInherit('push', '--follow-tags');
try {
execFileSync(
'gh',
['release', 'create', next, '--title', next, '--generate-notes'],
{ cwd: root, stdio: 'inherit' },
);
} catch {
console.error(`The tag ${next} is pushed but the GitHub release could not be created.`);
console.error(`Fix the GitHub CLI login and run: gh release create ${next} --title ${next} --generate-notes`);
process.exit(1);
}
console.log(`Released ${next}`);