Files
opencode-mobile/scripts/check-version-parity.mjs
Den c43ec27a8c fix(release): give v0.4.15 its own versionCode so the release can actually ship (#180)
The v0.4.15 release commit (f564869) bumped package.json and app.json
`expo.version` but left `android/app/build.gradle` at versionCode 41 /
versionName "0.4.14" and app.json `android.versionCode` at 41. Consequences,
all observed on tag v0.4.15:

- `npm run check:versions` fails, so the F-Droid publish (run 31820921979)
  died at its first step and the `release` job in build.yml never ran — no
  GitHub release exists for v0.4.15. That is the source the new in-app update
  check polls, so the mechanism this release exists to ship had nothing to
  find.
- versionCode 41 is v0.4.14's. F-Droid and every direct-APK install key
  upgrades off versionCode, so even a successful publish would not have been
  offered to the 0.4.10/0.4.14 cohort. Play was unaffected only because the
  publish workflow overrides the code with run_number+100.

Fix is the missing half of the release bump: versionCode 42 / versionName
0.4.15, plus the changelog files named after the code (distribution/ for the
record, fastlane/ for F-Droid).

check-version-parity.mjs now also requires distribution/changelogs/<code>.txt
to exist and to describe the version being released, and the fastlane copy to
exist. A stale versionCode is otherwise internally consistent and silent;
verified it discriminates — code 41 with version 0.4.15 fails, 42 passes.

Tests: npm test 320 pass.

Co-authored-by: engineer <engineer@macbookpro.lan>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-14 10:33:38 -07:00

63 lines
2.4 KiB
JavaScript

import { readFile } from "node:fs/promises";
const app = JSON.parse(await readFile("app.json", "utf8"));
const pkg = JSON.parse(await readFile("package.json", "utf8"));
const gradle = await readFile("android/app/build.gradle", "utf8");
const name = gradle.match(/^\s*versionName\s+"([^"]+)"/m)?.[1];
const code = Number(gradle.match(/^\s*versionCode\s+(\d+)/m)?.[1]);
const expectedName = app.expo.version;
const expectedCode = app.expo.android.versionCode;
const errors = [];
if (pkg.version !== expectedName) {
errors.push(`package.json version ${pkg.version} != app.json version ${expectedName}`);
}
if (name !== expectedName) {
errors.push(`Gradle versionName ${name ?? "missing"} != app.json version ${expectedName}`);
}
if (code !== expectedCode) {
errors.push(`Gradle versionCode ${Number.isNaN(code) ? "missing" : code} != app.json versionCode ${expectedCode}`);
}
// A versionCode left at the previous release's value is the silent half of this
// failure: version metadata is internally consistent, Play still accepts the
// upload (CI overrides the code with run_number+100), but the F-Droid repo and
// every direct-APK install key upgrades off versionCode — so a 0.4.15 APK
// carrying 0.4.14's code is never offered to the cohort it was cut for.
// The changelog file is named after the versionCode and its first line names
// the version, so requiring the two to agree pins the code to this release.
const changelogPath = `distribution/changelogs/${expectedCode}.txt`;
let changelog = null;
try {
changelog = await readFile(changelogPath, "utf8");
} catch {
errors.push(`${changelogPath} is missing — every release needs a changelog named after its versionCode`);
}
if (changelog !== null) {
const firstLine = changelog.split("\n", 1)[0].trim();
if (!firstLine.startsWith(`v${expectedName}`)) {
errors.push(
`${changelogPath} describes "${firstLine}", not v${expectedName} — versionCode ${expectedCode} belongs to an earlier release, so bump it`,
);
}
}
const fastlanePath = `fastlane/metadata/android/en-US/changelogs/${expectedCode}.txt`;
try {
await readFile(fastlanePath, "utf8");
} catch {
errors.push(`${fastlanePath} is missing — F-Droid reads its release notes from here`);
}
if (errors.length) {
console.error(errors.join("\n"));
process.exitCode = 1;
} else {
console.log(`Version metadata aligned: ${expectedName} (${expectedCode})`);
}