|
| 1 | +'use strict'; |
| 2 | + |
| 3 | +/** |
| 4 | + * Refresh the example iOS CocoaPods lockfile so release commits keep |
| 5 | + * RNGoogleMobileAdsExample/ios/Podfile.lock aligned with packages/core |
| 6 | + * (pod version + sdkVersions.ios pins). Mirrors the Darwin path in RNFB |
| 7 | + * scripts/version.js, adapted for semantic-release. |
| 8 | + * |
| 9 | + * Usage: |
| 10 | + * node ./scripts/refresh-ios-pod-lockfile.js |
| 11 | + * node ./scripts/refresh-ios-pod-lockfile.js --assert-pins-only |
| 12 | + * node ./scripts/refresh-ios-pod-lockfile.js --self-check |
| 13 | + * yarn release:refresh-ios-pod-lockfile |
| 14 | + */ |
| 15 | + |
| 16 | +const { execSync } = require('child_process'); |
| 17 | +const { existsSync, readFileSync } = require('fs'); |
| 18 | +const path = require('path'); |
| 19 | + |
| 20 | +const REPO_ROOT = path.resolve(__dirname, '..'); |
| 21 | +const PACKAGE_JSON_PATH = path.join(REPO_ROOT, 'packages/core/package.json'); |
| 22 | +const PODFILE_LOCK_PATH = path.join(REPO_ROOT, 'RNGoogleMobileAdsExample/ios/Podfile.lock'); |
| 23 | +const POD_LOCKFILE_RELATIVE = 'RNGoogleMobileAdsExample/ios/Podfile.lock'; |
| 24 | + |
| 25 | +function readCorePackage(packageJsonPath = PACKAGE_JSON_PATH) { |
| 26 | + return JSON.parse(readFileSync(packageJsonPath, 'utf8')); |
| 27 | +} |
| 28 | + |
| 29 | +function extractLockedPodVersion(lockfileContents, podName) { |
| 30 | + // Top-level PODS entries use exactly two spaces before `-` (nested deps use four+). |
| 31 | + const escaped = podName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); |
| 32 | + const match = lockfileContents.match(new RegExp(`^ - ${escaped} \\(([^)]+)\\)`, 'm')); |
| 33 | + return match ? match[1] : undefined; |
| 34 | +} |
| 35 | + |
| 36 | +function assertPinsMatch(packageJson, lockfileContents) { |
| 37 | + const expectedPackageVersion = packageJson.version; |
| 38 | + const expectedGma = packageJson.sdkVersions && packageJson.sdkVersions.ios.googleMobileAds; |
| 39 | + const expectedUmp = packageJson.sdkVersions && packageJson.sdkVersions.ios.googleUmp; |
| 40 | + |
| 41 | + if (!expectedPackageVersion || !expectedGma || !expectedUmp) { |
| 42 | + throw new Error( |
| 43 | + 'packages/core/package.json missing version or sdkVersions.ios.googleMobileAds/googleUmp', |
| 44 | + ); |
| 45 | + } |
| 46 | + |
| 47 | + const lockedPackage = extractLockedPodVersion(lockfileContents, 'RNGoogleMobileAds'); |
| 48 | + const lockedGma = extractLockedPodVersion(lockfileContents, 'Google-Mobile-Ads-SDK'); |
| 49 | + const lockedUmp = extractLockedPodVersion(lockfileContents, 'GoogleUserMessagingPlatform'); |
| 50 | + |
| 51 | + const mismatches = []; |
| 52 | + if (lockedPackage !== expectedPackageVersion) { |
| 53 | + mismatches.push( |
| 54 | + `RNGoogleMobileAds lock=${lockedPackage} package.json=${expectedPackageVersion}`, |
| 55 | + ); |
| 56 | + } |
| 57 | + if (lockedGma !== expectedGma) { |
| 58 | + mismatches.push( |
| 59 | + `Google-Mobile-Ads-SDK lock=${lockedGma} sdkVersions.ios.googleMobileAds=${expectedGma}`, |
| 60 | + ); |
| 61 | + } |
| 62 | + if (lockedUmp !== expectedUmp) { |
| 63 | + mismatches.push( |
| 64 | + `GoogleUserMessagingPlatform lock=${lockedUmp} sdkVersions.ios.googleUmp=${expectedUmp}`, |
| 65 | + ); |
| 66 | + } |
| 67 | + |
| 68 | + if (mismatches.length > 0) { |
| 69 | + throw new Error( |
| 70 | + `Podfile.lock pins out of sync with packages/core:\n- ${mismatches.join('\n- ')}`, |
| 71 | + ); |
| 72 | + } |
| 73 | + |
| 74 | + return { |
| 75 | + packageVersion: expectedPackageVersion, |
| 76 | + googleMobileAds: expectedGma, |
| 77 | + googleUmp: expectedUmp, |
| 78 | + }; |
| 79 | +} |
| 80 | + |
| 81 | +function assertCurrentPins() { |
| 82 | + if (!existsSync(PACKAGE_JSON_PATH)) { |
| 83 | + throw new Error(`missing ${PACKAGE_JSON_PATH}`); |
| 84 | + } |
| 85 | + if (!existsSync(PODFILE_LOCK_PATH)) { |
| 86 | + throw new Error(`missing ${PODFILE_LOCK_PATH}`); |
| 87 | + } |
| 88 | + return assertPinsMatch(readCorePackage(), readFileSync(PODFILE_LOCK_PATH, 'utf8')); |
| 89 | +} |
| 90 | + |
| 91 | +function runPodInstall() { |
| 92 | + execSync('yarn tests:ios:pod:install', { cwd: REPO_ROOT, stdio: 'inherit' }); |
| 93 | +} |
| 94 | + |
| 95 | +function stageAndVerifyIdempotent() { |
| 96 | + execSync(`git add -- ${POD_LOCKFILE_RELATIVE}`, { cwd: REPO_ROOT, stdio: 'inherit' }); |
| 97 | + runPodInstall(); |
| 98 | + execSync(`git diff --exit-code -- ${POD_LOCKFILE_RELATIVE}`, { |
| 99 | + cwd: REPO_ROOT, |
| 100 | + stdio: 'inherit', |
| 101 | + }); |
| 102 | +} |
| 103 | + |
| 104 | +function refreshIosPodLockfile(options = {}) { |
| 105 | + const { requireDarwin = true } = options; |
| 106 | + |
| 107 | + if (requireDarwin && process.platform !== 'darwin') { |
| 108 | + throw new Error( |
| 109 | + 'refresh-ios-pod-lockfile requires Darwin (CocoaPods). Publish CI must run on macOS.', |
| 110 | + ); |
| 111 | + } |
| 112 | + |
| 113 | + console.log('Refreshing example iOS Podfile.lock via yarn tests:ios:pod:install…'); |
| 114 | + runPodInstall(); |
| 115 | + const pins = assertCurrentPins(); |
| 116 | + console.log( |
| 117 | + `Pins OK after refresh: RNGoogleMobileAds=${pins.packageVersion} GMA=${pins.googleMobileAds} UMP=${pins.googleUmp}`, |
| 118 | + ); |
| 119 | + |
| 120 | + // Second install verifies the staged lockfile is idempotent (RNFB #9208 pattern). |
| 121 | + stageAndVerifyIdempotent(); |
| 122 | + console.log(`Idempotent: ${POD_LOCKFILE_RELATIVE}`); |
| 123 | + return pins; |
| 124 | +} |
| 125 | + |
| 126 | +function selfCheck() { |
| 127 | + const sampleLock = ` |
| 128 | +PODS: |
| 129 | + - Google-Mobile-Ads-SDK (13.5.0): |
| 130 | + - GoogleUserMessagingPlatform (>= 1.1) |
| 131 | + - GoogleUserMessagingPlatform (3.1.0) |
| 132 | + - RNGoogleMobileAds (16.5.0): |
| 133 | +`; |
| 134 | + const okPkg = { |
| 135 | + version: '16.5.0', |
| 136 | + sdkVersions: { ios: { googleMobileAds: '13.5.0', googleUmp: '3.1.0' } }, |
| 137 | + }; |
| 138 | + const badPkg = { |
| 139 | + version: '16.5.0', |
| 140 | + sdkVersions: { ios: { googleMobileAds: '13.1.0', googleUmp: '3.1.0' } }, |
| 141 | + }; |
| 142 | + |
| 143 | + assertPinsMatch(okPkg, sampleLock); |
| 144 | + let rejected = false; |
| 145 | + try { |
| 146 | + assertPinsMatch(badPkg, sampleLock); |
| 147 | + } catch (error) { |
| 148 | + rejected = /Google-Mobile-Ads-SDK/.test(String(error && error.message)); |
| 149 | + } |
| 150 | + if (!rejected) { |
| 151 | + throw new Error('self-check failed: drifted GMA pin must reject'); |
| 152 | + } |
| 153 | + |
| 154 | + if (extractLockedPodVersion(sampleLock, 'RNGoogleMobileAds') !== '16.5.0') { |
| 155 | + throw new Error('self-check failed: RNGoogleMobileAds extract'); |
| 156 | + } |
| 157 | + |
| 158 | + console.log('refresh-ios-pod-lockfile self-check: ok'); |
| 159 | +} |
| 160 | + |
| 161 | +function main(argv = process.argv.slice(2)) { |
| 162 | + if (argv.includes('--self-check')) { |
| 163 | + selfCheck(); |
| 164 | + return; |
| 165 | + } |
| 166 | + if (argv.includes('--assert-pins-only')) { |
| 167 | + const pins = assertCurrentPins(); |
| 168 | + console.log( |
| 169 | + `Pins OK: RNGoogleMobileAds=${pins.packageVersion} GMA=${pins.googleMobileAds} UMP=${pins.googleUmp}`, |
| 170 | + ); |
| 171 | + return; |
| 172 | + } |
| 173 | + refreshIosPodLockfile(); |
| 174 | +} |
| 175 | + |
| 176 | +module.exports = { |
| 177 | + POD_LOCKFILE_RELATIVE, |
| 178 | + assertPinsMatch, |
| 179 | + assertCurrentPins, |
| 180 | + extractLockedPodVersion, |
| 181 | + refreshIosPodLockfile, |
| 182 | + selfCheck, |
| 183 | +}; |
| 184 | + |
| 185 | +if (require.main === module) { |
| 186 | + try { |
| 187 | + main(); |
| 188 | + } catch (error) { |
| 189 | + console.error(error && error.message ? error.message : error); |
| 190 | + process.exit(1); |
| 191 | + } |
| 192 | +} |
0 commit comments