const { withDangerousMod } = require('@expo/config-plugins'); const fs = require('fs'); const path = require('path'); const withSigning = (config, options = {}) => { const { teamId = 'X3964MGXHG' } = options; config = withDangerousMod(config, [ 'ios', async (config) => { const platformRoot = config.modRequest.platformProjectRoot; // 1. Inject CODE_SIGN_STYLE and DEVELOPMENT_TEAM into pbxproj const pbxprojPath = path.join(platformRoot, 'app.xcodeproj', 'project.pbxproj'); if (fs.existsSync(pbxprojPath)) { let contents = fs.readFileSync(pbxprojPath, 'utf-8'); const entitlementsLine = 'CODE_SIGN_ENTITLEMENTS = app/app.entitlements;'; const signingBlock = `${entitlementsLine}\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = ${teamId};`; contents = contents.replaceAll(entitlementsLine, signingBlock); fs.writeFileSync(pbxprojPath, contents); } // 2. Set aps-environment to production in entitlements const entitlementsPath = path.join(platformRoot, 'app', 'app.entitlements'); if (fs.existsSync(entitlementsPath)) { let entitlements = fs.readFileSync(entitlementsPath, 'utf-8'); entitlements = entitlements.replace( /aps-environment<\/key>\s*development<\/string>/, 'aps-environment\n\tproduction' ); fs.writeFileSync(entitlementsPath, entitlements); } return config; }, ]); config = withDangerousMod(config, [ 'android', async (config) => { const platformRoot = config.modRequest.platformProjectRoot; const buildGradlePath = path.join(platformRoot, 'app', 'build.gradle'); if (!fs.existsSync(buildGradlePath)) return config; let lines = fs.readFileSync(buildGradlePath, 'utf-8').split('\n'); // 1. Add release signing config that reads from gradle.properties const hasReleaseSigning = lines.some((l) => l.includes('MYAPP_UPLOAD_STORE_FILE')); if (!hasReleaseSigning) { // Find the debug signingConfig closing brace and insert release after it let debugBraceLine = -1; let inSigningConfigs = false; let braceDepth = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.includes('signingConfigs {') || line.match(/^\s*signingConfigs\s*\{/)) { inSigningConfigs = true; braceDepth = 1; continue; } if (inSigningConfigs) { braceDepth += (line.match(/\{/g) || []).length; braceDepth -= (line.match(/\}/g) || []).length; if (braceDepth === 0) { debugBraceLine = i; // This is the closing } of signingConfigs break; } } } if (debugBraceLine > 0) { const releaseBlock = [ ' release {', " if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {", ' storeFile file(MYAPP_UPLOAD_STORE_FILE)', ' storePassword MYAPP_UPLOAD_STORE_PASSWORD', ' keyAlias MYAPP_UPLOAD_KEY_ALIAS', ' keyPassword MYAPP_UPLOAD_KEY_PASSWORD', ' }', ' }', ]; lines.splice(debugBraceLine, 0, ...releaseBlock); } } // 2. Change release buildType to use release signingConfig // Walk through buildTypes > release and replace signingConfigs.debug -> signingConfigs.release let inBuildTypes = false; let inRelease = false; let buildTypesDepth = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.includes('buildTypes {') || line.match(/^\s*buildTypes\s*\{/)) { inBuildTypes = true; buildTypesDepth = 1; continue; } if (inBuildTypes) { buildTypesDepth += (line.match(/\{/g) || []).length; buildTypesDepth -= (line.match(/\}/g) || []).length; if (line.match(/^\s*release\s*\{/)) { inRelease = true; } if (inRelease && line.includes('signingConfig signingConfigs.debug')) { lines[i] = line.replace('signingConfig signingConfigs.debug', 'signingConfig signingConfigs.release'); inRelease = false; } if (buildTypesDepth === 0) { inBuildTypes = false; inRelease = false; } } } fs.writeFileSync(buildGradlePath, lines.join('\n')); return config; }, ]); return config; }; module.exports = withSigning;