Bring the checklist app under version control
The project has lived only as a folder on the Mac Studio and, briefly, as a transfer tarball. This is the first commit: the Express server and its web front end, the Capacitor iOS and Android wrappers, and the release and backup scripts. The repo is the source, not a backup of the machine. Left out on purpose, by the .gitignore files that were already in the tree: node_modules, build output, mobile/www (regenerated from server/public on every build), and server/data, which holds the live checklists and belongs on the NAS rather than in git. Every credential is excluded too — server/.env, the Play upload keystore and its properties file, the App Store Connect key and ios-signing.config. The one secret that was not in an ignored file was the NAS SSH password, hard-coded in build-deploy.sh and grepped back out of it by backup-to-drive.sh. Both now read it from the login keychain (service checklist-nas, account Christian), falling back to NAS_PASS in the environment, so rotating it is one keychain entry rather than a history rewrite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
// stamp-version.js — push release.config.json's numbers into the Xcode project.
|
||||
//
|
||||
// Android reads release.config.json directly from build.gradle, but Xcode keeps
|
||||
// MARKETING_VERSION / CURRENT_PROJECT_VERSION inside project.pbxproj, so they have to be
|
||||
// written in. Both stores then get the same marketing version from one edited file, which
|
||||
// is the whole point: a build number that silently disagrees between platforms is how you
|
||||
// end up with "1.4 on Android, 1.2 on iPhone" bug reports that nobody can reproduce.
|
||||
//
|
||||
// Run with --check to verify without writing (used by release.sh as a pre-flight).
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const MOBILE = path.resolve(__dirname, '..');
|
||||
const PBXPROJ = path.join(MOBILE, 'ios', 'App', 'App.xcodeproj', 'project.pbxproj');
|
||||
|
||||
const release = JSON.parse(fs.readFileSync(path.join(MOBILE, 'release.config.json'), 'utf8'));
|
||||
const version = String(release.version || '').trim();
|
||||
const iosBuild = release.iosBuild;
|
||||
|
||||
// Apple rejects a CFBundleShortVersionString that isn't 1-3 dot-separated integers, and it
|
||||
// rejects it at upload time — after the whole archive has already been built and signed.
|
||||
if (!/^\d+(\.\d+){0,2}$/.test(version)) {
|
||||
console.error(`✗ version must be 1-3 dot-separated numbers (e.g. 1.2.3) — got ${JSON.stringify(release.version)}`);
|
||||
process.exit(1);
|
||||
}
|
||||
if (!Number.isInteger(iosBuild) || iosBuild < 1) {
|
||||
console.error(`✗ iosBuild must be a positive integer — got ${JSON.stringify(iosBuild)}`);
|
||||
process.exit(1);
|
||||
}
|
||||
if (!Number.isInteger(release.androidVersionCode) || release.androidVersionCode < 1) {
|
||||
console.error(`✗ androidVersionCode must be a positive integer — got ${JSON.stringify(release.androidVersionCode)}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let pbx = fs.readFileSync(PBXPROJ, 'utf8');
|
||||
const before = pbx;
|
||||
|
||||
// Both the Debug and Release configurations carry these, hence the global replace: an
|
||||
// archive built from the wrong configuration must not ship a stale build number.
|
||||
pbx = pbx.replace(/MARKETING_VERSION = [^;]+;/g, `MARKETING_VERSION = ${version};`);
|
||||
pbx = pbx.replace(/CURRENT_PROJECT_VERSION = [^;]+;/g, `CURRENT_PROJECT_VERSION = ${iosBuild};`);
|
||||
|
||||
const marketingHits = (pbx.match(/MARKETING_VERSION = /g) || []).length;
|
||||
const buildHits = (pbx.match(/CURRENT_PROJECT_VERSION = /g) || []).length;
|
||||
if (marketingHits < 2 || buildHits < 2) {
|
||||
console.error(`✗ expected MARKETING_VERSION and CURRENT_PROJECT_VERSION in both build configs (found ${marketingHits}/${buildHits})`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (process.argv.includes('--check')) {
|
||||
console.log(pbx === before
|
||||
? `✓ Xcode already at ${version} (${iosBuild})`
|
||||
: `• Xcode would change to ${version} (${iosBuild})`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (pbx !== before) fs.writeFileSync(PBXPROJ, pbx);
|
||||
console.log(`✓ Xcode stamped ${version} (build ${iosBuild})`);
|
||||
console.log(`✓ Gradle reads ${version} (code ${release.androidVersionCode}) from release.config.json`);
|
||||
Reference in New Issue
Block a user