2026-05-12 01:28:46 +08:00
|
|
|
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(
|
|
|
|
|
/<key>aps-environment<\/key>\s*<string>development<\/string>/,
|
|
|
|
|
'<key>aps-environment</key>\n\t<string>production</string>'
|
|
|
|
|
);
|
|
|
|
|
fs.writeFileSync(entitlementsPath, entitlements);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
|
2026-06-15 20:43:15 +08:00
|
|
|
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;
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
|
2026-05-12 01:28:46 +08:00
|
|
|
return config;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-15 20:43:15 +08:00
|
|
|
module.exports = withSigning;
|