chore(watch): automate SentrySPM integration via post-prebuild script
This commit is contained in:
@@ -8,13 +8,14 @@ Swimming workout tracker with Apple Watch and WearOS companion apps. Expo 57 + R
|
||||
npx expo start # Dev server
|
||||
npx expo run:ios # Build & run on iOS
|
||||
npx expo prebuild -p ios # Regenerate ios/ dir (safe — targets/ persists)
|
||||
npx expo prebuild -p ios && node scripts/add-sentry-watch.js # Prebuild + add SentrySPM to watch target
|
||||
npx drizzle-kit generate # Regenerate SQL migrations after schema changes
|
||||
```
|
||||
|
||||
**WearOS:**
|
||||
```bash
|
||||
./scripts/build-wearos.sh debug # Build debug APK
|
||||
./scripts/build-wearos.sh release # Build release APK
|
||||
./scripts/build-wearos.sh debug # Build debug APK (reads version from app.json)
|
||||
./scripts/build-wearos.sh release # Build release APK (reads version from app.json)
|
||||
./scripts/generate-wearos-icon.sh assets/icon.png # Generate launcher icons
|
||||
```
|
||||
|
||||
@@ -58,6 +59,7 @@ targets/wearos/ # WearOS app (Kotlin/Compose) — survives prebuil
|
||||
- WCSession for real-time messaging, `transferUserInfo` for reliable offline sync
|
||||
- HealthKit for swim workout tracking (indoor swimming)
|
||||
- Swift types `StoredWorkout`, `StoredSet`, `CompletedLap` in `WorkoutManager.swift`
|
||||
- SentrySPM: `@bacons/apple-targets` doesn't support `swiftPackages` yet. Run `node scripts/add-sentry-watch.js` after `expo prebuild` to add it automatically. For EAS builds, this runs automatically via the `eas-build-post-install` hook in package.json.
|
||||
|
||||
## WearOS Target
|
||||
|
||||
|
||||
+3
-2
@@ -43,8 +43,9 @@
|
||||
"ios": "expo run:ios",
|
||||
"web": "expo start --web",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"prebuild": "expo prebuild",
|
||||
"prebuild:clean": "expo prebuild --clean",
|
||||
"prebuild": "expo prebuild && npm run eas-build-post-install",
|
||||
"prebuild:clean": "expo prebuild --clean && npm run eas-build-post-install",
|
||||
"eas-build-post-install": "node scripts/add-sentry-watch.js",
|
||||
"build:wearos:debug": "./scripts/build-wearos.sh debug",
|
||||
"build:wearos:release": "./scripts/build-wearos.sh release",
|
||||
"deploy:ios": "eas build --platform ios --profile ${1:-preview} && eas submit --platform ios --profile ${1:-preview}",
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Post-prebuild script: adds SentrySPM Swift Package to the watch target.
|
||||
*
|
||||
* Run after `expo prebuild`:
|
||||
* node scripts/add-sentry-watch.js
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const SENTRY_REPO_URL = "https://github.com/getsentry/sentry-cocoa.git";
|
||||
const SENTRY_MIN_VERSION = "9.25.0";
|
||||
const SENTRY_PRODUCT = "Sentry";
|
||||
|
||||
function genUuid() {
|
||||
const hex = "0123456789ABCDEF";
|
||||
let id = "";
|
||||
for (let i = 0; i < 24; i++) id += hex[Math.floor(Math.random() * 16)];
|
||||
return id;
|
||||
}
|
||||
|
||||
const projectRoot = path.resolve(__dirname, "..");
|
||||
const pbxprojPath = path.join(
|
||||
projectRoot,
|
||||
"ios",
|
||||
"SwimBuddy.xcodeproj",
|
||||
"project.pbxproj"
|
||||
);
|
||||
|
||||
if (!fs.existsSync(pbxprojPath)) {
|
||||
console.error("project.pbxproj not found. Run expo prebuild first.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let content = fs.readFileSync(pbxprojPath, "utf-8");
|
||||
|
||||
// Already added?
|
||||
if (content.includes(SENTRY_REPO_URL)) {
|
||||
console.log("SentrySPM already present.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// ── Find watch target UUID ──
|
||||
const watchTargetMatch = content.match(
|
||||
/(\w{24})\s*\/\* SwimBuddy \*\/\s*=\s*\{\s*\n\s*isa = PBXNativeTarget;\s*\n\s*buildConfigurationList = XX792EB48D81CD7C6C71DCXX/
|
||||
);
|
||||
if (!watchTargetMatch) {
|
||||
console.error("Watch target not found");
|
||||
process.exit(1);
|
||||
}
|
||||
const watchTargetUuid = watchTargetMatch[1];
|
||||
console.log(`Watch target: ${watchTargetUuid}`);
|
||||
|
||||
// ── Find watch target's Frameworks build phase UUID ──
|
||||
const watchTargetBlock = content.match(
|
||||
new RegExp(
|
||||
`${watchTargetUuid}\\s*\\/\\* SwimBuddy \\*\\/\\s*=\\s*\\{[\\s\\S]*?buildPhases\\s*=\\s*\\(([\\s\\S]*?)\\);`
|
||||
)
|
||||
);
|
||||
if (!watchTargetBlock) {
|
||||
console.error("Cannot parse watch target buildPhases");
|
||||
process.exit(1);
|
||||
}
|
||||
const fwPhaseMatch = watchTargetBlock[1].match(/(\w{24})\s*\/\* Frameworks \*\//);
|
||||
if (!fwPhaseMatch) {
|
||||
console.error("Watch Frameworks phase not found");
|
||||
process.exit(1);
|
||||
}
|
||||
const watchFwPhaseId = fwPhaseMatch[1];
|
||||
console.log(`Watch Frameworks phase: ${watchFwPhaseId}`);
|
||||
|
||||
// ── Generate UUIDs ──
|
||||
const pkgRefId = genUuid();
|
||||
const prodDepId = genUuid();
|
||||
const buildFileId = genUuid();
|
||||
|
||||
// ── 1. PBXBuildFile (single-line format) ──
|
||||
const buildFileLine = `\t\t${buildFileId} /* ${SENTRY_PRODUCT} in Frameworks */ = {isa = PBXBuildFile; productRef = ${prodDepId} /* ${SENTRY_PRODUCT} */; };`;
|
||||
content = content.replace(
|
||||
/\/\* End PBXBuildFile section \*\//,
|
||||
`${buildFileLine}\n/* End PBXBuildFile section */`
|
||||
);
|
||||
|
||||
// ── 2. XCRemoteSwiftPackageReference section ──
|
||||
const pkgRefLine = `\t\t${pkgRefId} /* XCRemoteSwiftPackageReference "sentry-cocoa" */ = {isa = XCRemoteSwiftPackageReference; repositoryURL = "${SENTRY_REPO_URL}"; requirement = {kind = "upToNextMajorVersion"; minimumVersion = "${SENTRY_MIN_VERSION}"; }; };`;
|
||||
|
||||
if (!content.includes("/* Begin XCRemoteSwiftPackageReference section */")) {
|
||||
content = content.replace(
|
||||
/\/\* End PBXProject section \*\//,
|
||||
`/* Begin XCRemoteSwiftPackageReference section */\n${pkgRefLine}\n/* End XCRemoteSwiftPackageReference section */\n/* End PBXProject section */`
|
||||
);
|
||||
} else {
|
||||
content = content.replace(
|
||||
/\/\* End XCRemoteSwiftPackageReference section \*\//,
|
||||
`${pkgRefLine}\n/* End XCRemoteSwiftPackageReference section */`
|
||||
);
|
||||
}
|
||||
|
||||
// ── 3. XCSwiftPackageProductDependency section ──
|
||||
const prodDepLine = `\t\t${prodDepId} /* ${SENTRY_PRODUCT} */ = {isa = XCSwiftPackageProductDependency; package = ${pkgRefId} /* XCRemoteSwiftPackageReference "sentry-cocoa" */; productName = ${SENTRY_PRODUCT}; };`;
|
||||
|
||||
if (!content.includes("/* Begin XCSwiftPackageProductDependency section */")) {
|
||||
content = content.replace(
|
||||
/\/\* End XCRemoteSwiftPackageReference section \*\//,
|
||||
`/* Begin XCSwiftPackageProductDependency section */\n${prodDepLine}\n/* End XCSwiftPackageProductDependency section */\n/* End XCRemoteSwiftPackageReference section */`
|
||||
);
|
||||
} else {
|
||||
content = content.replace(
|
||||
/\/\* End XCSwiftPackageProductDependency section \*\//,
|
||||
`${prodDepLine}\n/* End XCSwiftPackageProductDependency section */`
|
||||
);
|
||||
}
|
||||
|
||||
// ── 4. Add to watch Frameworks build phase ──
|
||||
const fwPhasePattern = new RegExp(
|
||||
`(${watchFwPhaseId}\\s*\\/\\* Frameworks \\*\\/ = \\{[\\s\\S]*?files = \\()[\\s\\S]*?(\\);[\\s\\S]*?runOnlyForDeploymentPostprocessing)`
|
||||
);
|
||||
const fwMatch = content.match(fwPhasePattern);
|
||||
if (fwMatch) {
|
||||
const insertLine = `\n\t\t\t\t${buildFileId} /* ${SENTRY_PRODUCT} in Frameworks */,`;
|
||||
content = content.replace(fwPhasePattern, `$1${fwMatch[0].includes(buildFileId) ? "" : insertLine}$2`);
|
||||
} else {
|
||||
console.error("Cannot insert into watch Frameworks build phase");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ── 5. packageReferences in PBXProject ──
|
||||
const pkgRefComment = `${pkgRefId} /* XCRemoteSwiftPackageReference "sentry-cocoa" */`;
|
||||
if (!content.includes("packageReferences = (")) {
|
||||
content = content.replace(
|
||||
/(targets\s*=\s*\([^)]*\);)/,
|
||||
`$1\n\t\t\tpackageReferences = (\n\t\t\t\t${pkgRefComment},\n\t\t\t);`
|
||||
);
|
||||
} else {
|
||||
content = content.replace(
|
||||
/(packageReferences\s*=\s*\()/,
|
||||
`$1\n\t\t\t\t${pkgRefComment},`
|
||||
);
|
||||
}
|
||||
|
||||
// ── 6. packageProductDependencies on watch target ──
|
||||
const prodDepComment = `${prodDepId} /* ${SENTRY_PRODUCT} */`;
|
||||
// Find the watch target block and check if packageProductDependencies exists
|
||||
const wtPattern = new RegExp(
|
||||
`(${watchTargetUuid}\\s*\\/\\* SwimBuddy \\*\\/ = \\{[\\s\\S]*?fileSystemSynchronizedGroups = \\([\\s\\S]*?\\);[\\s\\S]*?)(name = )`
|
||||
);
|
||||
const wtMatch = content.match(wtPattern);
|
||||
if (wtMatch && !content.includes("packageProductDependencies")) {
|
||||
content = content.replace(
|
||||
wtPattern,
|
||||
`$1\n\t\t\tpackageProductDependencies = (\n\t\t\t\t${prodDepComment},\n\t\t\t);\n\t\t$2`
|
||||
);
|
||||
}
|
||||
|
||||
fs.writeFileSync(pbxprojPath, content);
|
||||
console.log("SentrySPM added to watch target successfully.");
|
||||
@@ -5,14 +5,6 @@ module.exports = (config) => ({
|
||||
icon: "../../assets/icon.png",
|
||||
deploymentTarget: "10.0",
|
||||
frameworks: ["SwiftUI"],
|
||||
swiftPackages: [
|
||||
{
|
||||
name: "SentrySPM",
|
||||
repositoryURL: "https://github.com/getsentry/sentry-cocoa.git",
|
||||
kind: "upToNextMajorVersion",
|
||||
minimumVersion: "9.25.0",
|
||||
},
|
||||
],
|
||||
entitlements: {
|
||||
"com.apple.security.application-groups":
|
||||
config.ios?.entitlements?.[
|
||||
|
||||
Reference in New Issue
Block a user