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>
63 lines
2.9 KiB
JavaScript
63 lines
2.9 KiB
JavaScript
#!/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`);
|