codeplay-common 4.8.7 → 4.8.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1366,7 +1366,7 @@ function syncAndroidCodeOptimization() {
|
|
|
1366
1366
|
}
|
|
1367
1367
|
|
|
1368
1368
|
const currentContent = fs.readFileSync(androidAppBuildGradlePath, 'utf8');
|
|
1369
|
-
|
|
1369
|
+
let nextContent = currentContent.replace(
|
|
1370
1370
|
/getDefaultProguardFile\(\s*(['"])proguard-android\.txt\1\s*\)/g,
|
|
1371
1371
|
"getDefaultProguardFile('proguard-android-optimize.txt')"
|
|
1372
1372
|
);
|
|
@@ -1375,6 +1375,27 @@ function syncAndroidCodeOptimization() {
|
|
|
1375
1375
|
throw new Error('Android code optimisation setup failed: expected default ProGuard configuration was not found.');
|
|
1376
1376
|
}
|
|
1377
1377
|
|
|
1378
|
+
const newline = currentContent.includes('\r\n') ? '\r\n' : '\n';
|
|
1379
|
+
nextContent = nextContent.replace(/^([ \t]*release[ \t]*\{)([\s\S]*?)(?=^[ \t]*\})/m, (match, header, body) => {
|
|
1380
|
+
const comments = body.match(/\/\/[^\r\n]*|\/\*[\s\S]*?\*\//g) || [];
|
|
1381
|
+
if (comments.some(comment => /\bshrinkResources\b/.test(comment))) return match;
|
|
1382
|
+
|
|
1383
|
+
const activeBody = body.replace(/\/\/[^\r\n]*|\/\*[\s\S]*?\*\//g, '');
|
|
1384
|
+
const minifyEnabled = /^[ \t]*minifyEnabled\s*(?:=\s*)?true\b/m.test(activeBody);
|
|
1385
|
+
const shrinkPattern = /^([ \t]*shrinkResources\s*(?:=\s*)?)(true|false)\b/m;
|
|
1386
|
+
if (shrinkPattern.test(body)) {
|
|
1387
|
+
return minifyEnabled ? match : header + body.replace(shrinkPattern, '$1false');
|
|
1388
|
+
}
|
|
1389
|
+
return `${header}${newline} shrinkResources ${minifyEnabled}${body}`;
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
const propertiesPath = path.join(androidPlatformPath, 'gradle.properties');
|
|
1393
|
+
const properties = fs.readFileSync(propertiesPath, 'utf8');
|
|
1394
|
+
// An explicit false value or a commented setting is an app-specific opt-out.
|
|
1395
|
+
if (!/^[ \t]*[#!]?[ \t]*android\.r8\.optimizedResourceShrinking(?:[ \t]*[=:]|[ \t]+|[ \t]*$)/m.test(properties)) {
|
|
1396
|
+
fs.writeFileSync(propertiesPath, `${properties.trimEnd()}${newline}android.r8.optimizedResourceShrinking=true${newline}`, 'utf8');
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1378
1399
|
if (nextContent !== currentContent) {
|
|
1379
1400
|
fs.writeFileSync(androidAppBuildGradlePath, nextContent, 'utf8');
|
|
1380
1401
|
}
|
|
@@ -4343,6 +4364,7 @@ await main();
|
|
|
4343
4364
|
Release Notes
|
|
4344
4365
|
|
|
4345
4366
|
6.5
|
|
4367
|
+
Release resource shrinking is configured automatically; commented settings and explicit false values preserve app-specific opt-outs.
|
|
4346
4368
|
Android builds use the optimisation-enabled default ProGuard rules without changing minification settings or custom keep rules.
|
|
4347
4369
|
Kids ad configuration disables iOS ATT prompts and excludes Android AD_ID during builds.
|
|
4348
4370
|
Non-kids apps retain tracking prompts and normal permission behavior.
|
package/files/finalrelease
CHANGED
|
@@ -7,7 +7,7 @@ node finalrelease "1,2,7"
|
|
|
7
7
|
|
|
8
8
|
import { existsSync, readFileSync, readdirSync, promises, constants, writeFileSync, unlinkSync, mkdirSync, renameSync } from 'fs';
|
|
9
9
|
import { join, resolve } from 'path';
|
|
10
|
-
import { execSync, execFileSync } from 'child_process';
|
|
10
|
+
import { execSync, execFileSync } from 'child_process';
|
|
11
11
|
import { createInterface } from 'readline';
|
|
12
12
|
|
|
13
13
|
import { fileURLToPath } from 'url';
|
|
@@ -179,49 +179,49 @@ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
|
179
179
|
|
|
180
180
|
const _appUniqueId = config.android?.APP_UNIQUE_ID;
|
|
181
181
|
const _appName=config.appName;
|
|
182
|
-
const _appPackageId=config.appId;
|
|
183
|
-
|
|
184
|
-
const releaseSyncConfigPath = join(__dirname, 'buildCodeplay', 'release-sync.config.json');
|
|
185
|
-
let releaseSyncConfig = {};
|
|
186
|
-
if (existsSync(releaseSyncConfigPath)) {
|
|
187
|
-
try {
|
|
188
|
-
releaseSyncConfig = JSON.parse(readFileSync(releaseSyncConfigPath, 'utf8'));
|
|
189
|
-
} catch (error) {
|
|
190
|
-
console.warn(`⚠️ Could not read ${releaseSyncConfigPath}: ${error.message}`);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
const RELEASE_SYNC_URL = process.env.CODEPLAY_RELEASE_SYNC_URL || releaseSyncConfig.url || 'https://htmlcodeplay.com/codeplay/api/v1/release/sync';
|
|
194
|
-
const RELEASE_SYNC_TOKEN = process.env.CODEPLAY_RELEASE_SYNC_TOKEN || releaseSyncConfig.token || '';
|
|
195
|
-
const RELEASE_PLATFORM_IDS = { PlayStore: 1, SamsungStore: 13, AmazonStore: 17, MiStore: 15, HuaweiStore: 12, OppoStore: 14, VivoStore: 16 };
|
|
196
|
-
|
|
197
|
-
function syncReleaseToCodeplay(storeName, finalVersionName, finalVersionCode, releasedDate) {
|
|
198
|
-
if (!RELEASE_SYNC_TOKEN) {
|
|
199
|
-
console.warn(`⚠️ Codeplay release sync skipped: add a token to ${releaseSyncConfigPath}.`);
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
const payload = JSON.stringify({
|
|
203
|
-
app_name: _appName,
|
|
204
|
-
package_id: _appPackageId,
|
|
205
|
-
version_name: String(finalVersionName),
|
|
206
|
-
version_code: String(finalVersionCode),
|
|
207
|
-
released_date: releasedDate,
|
|
208
|
-
language_id: 0,
|
|
209
|
-
language_name: 'Capacitor+ Framework 7',
|
|
210
|
-
notes: 'Automated release update',
|
|
211
|
-
status: 1,
|
|
212
|
-
folder_name: path.basename(process.cwd()),
|
|
213
|
-
app_unique_id: String(_appUniqueId || ''),
|
|
214
|
-
platform_id: RELEASE_PLATFORM_IDS[storeName],
|
|
215
|
-
});
|
|
216
|
-
try {
|
|
217
|
-
const response = execFileSync('curl.exe', ['-sS', '--fail-with-body', '-X', 'POST', RELEASE_SYNC_URL,
|
|
218
|
-
'-H', 'Content-Type: application/json', '-H', `X-Release-Token: ${RELEASE_SYNC_TOKEN}`, '--data-binary', payload],
|
|
219
|
-
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
220
|
-
console.log(`✅ Codeplay sync (${storeName}): ${response.trim()}`);
|
|
221
|
-
} catch (error) {
|
|
222
|
-
console.warn(`⚠️ Codeplay sync failed for ${storeName}; continuing release builds: ${error.message}`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
182
|
+
const _appPackageId=config.appId;
|
|
183
|
+
|
|
184
|
+
const releaseSyncConfigPath = join(__dirname, 'buildCodeplay', 'release-sync.config.json');
|
|
185
|
+
let releaseSyncConfig = {};
|
|
186
|
+
if (existsSync(releaseSyncConfigPath)) {
|
|
187
|
+
try {
|
|
188
|
+
releaseSyncConfig = JSON.parse(readFileSync(releaseSyncConfigPath, 'utf8'));
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.warn(`⚠️ Could not read ${releaseSyncConfigPath}: ${error.message}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const RELEASE_SYNC_URL = process.env.CODEPLAY_RELEASE_SYNC_URL || releaseSyncConfig.url || 'https://htmlcodeplay.com/codeplay/api/v1/release/sync';
|
|
194
|
+
const RELEASE_SYNC_TOKEN = process.env.CODEPLAY_RELEASE_SYNC_TOKEN || releaseSyncConfig.token || '';
|
|
195
|
+
const RELEASE_PLATFORM_IDS = { PlayStore: 1, SamsungStore: 13, AmazonStore: 17, MiStore: 15, HuaweiStore: 12, OppoStore: 14, VivoStore: 16 };
|
|
196
|
+
|
|
197
|
+
function syncReleaseToCodeplay(storeName, finalVersionName, finalVersionCode, releasedDate) {
|
|
198
|
+
if (!RELEASE_SYNC_TOKEN) {
|
|
199
|
+
console.warn(`⚠️ Codeplay release sync skipped: add a token to ${releaseSyncConfigPath}.`);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const payload = JSON.stringify({
|
|
203
|
+
app_name: _appName,
|
|
204
|
+
package_id: _appPackageId,
|
|
205
|
+
version_name: String(finalVersionName),
|
|
206
|
+
version_code: String(finalVersionCode),
|
|
207
|
+
released_date: releasedDate,
|
|
208
|
+
language_id: 0,
|
|
209
|
+
language_name: 'Capacitor+ Framework 7',
|
|
210
|
+
notes: 'Automated release update',
|
|
211
|
+
status: 1,
|
|
212
|
+
folder_name: path.basename(process.cwd()),
|
|
213
|
+
app_unique_id: String(_appUniqueId || ''),
|
|
214
|
+
platform_id: RELEASE_PLATFORM_IDS[storeName],
|
|
215
|
+
});
|
|
216
|
+
try {
|
|
217
|
+
const response = execFileSync('curl.exe', ['-sS', '--fail-with-body', '-X', 'POST', RELEASE_SYNC_URL,
|
|
218
|
+
'-H', 'Content-Type: application/json', '-H', `X-Release-Token: ${RELEASE_SYNC_TOKEN}`, '--data-binary', payload],
|
|
219
|
+
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
220
|
+
console.log(`✅ Codeplay sync (${storeName}): ${response.trim()}`);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.warn(`⚠️ Codeplay sync failed for ${storeName}; continuing release builds: ${error.message}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
225
|
|
|
226
226
|
const FLOWCHART_API_APP_ID = 228;
|
|
227
227
|
const FLOWCHART_SHARED_PATH = join('public', 'src', 'shared.js');
|
|
@@ -742,6 +742,93 @@ function findGradleBlock(content, headerPattern) {
|
|
|
742
742
|
return null;
|
|
743
743
|
}
|
|
744
744
|
|
|
745
|
+
function ensureReleaseCodeOptimization() {
|
|
746
|
+
const currentContent = readFileSync(gradleFilePath, 'utf8');
|
|
747
|
+
const releaseBlock = findGradleBlock(currentContent, /buildTypes\s*\{[\s\S]*?\brelease\s*/);
|
|
748
|
+
if (!releaseBlock) throw new Error('Release build configuration was not found.');
|
|
749
|
+
|
|
750
|
+
const releaseContent = currentContent.slice(releaseBlock.start, releaseBlock.end);
|
|
751
|
+
let optimizedContent = releaseContent.replace(
|
|
752
|
+
/getDefaultProguardFile\(\s*(['"])proguard-android\.txt\1\s*\)/g,
|
|
753
|
+
"getDefaultProguardFile('proguard-android-optimize.txt')"
|
|
754
|
+
);
|
|
755
|
+
if (!/getDefaultProguardFile\(\s*(['"])proguard-android-optimize\.txt\1\s*\)/.test(optimizedContent)) {
|
|
756
|
+
throw new Error('Release build must use the optimisation-enabled default ProGuard rules.');
|
|
757
|
+
}
|
|
758
|
+
const newline = currentContent.includes('\r\n') ? '\r\n' : '\n';
|
|
759
|
+
// Preserve commented settings and explicit false values as app-specific opt-outs.
|
|
760
|
+
const comments = optimizedContent.match(/\/\/[^\r\n]*|\/\*[\s\S]*?\*\//g) || [];
|
|
761
|
+
if (!comments.some(comment => /\bshrinkResources\b/.test(comment))) {
|
|
762
|
+
const activeContent = optimizedContent.replace(/\/\/[^\r\n]*|\/\*[\s\S]*?\*\//g, '');
|
|
763
|
+
const shrinkResources = /^[ \t]*minifyEnabled\s*(?:=\s*)?true\b/m.test(activeContent);
|
|
764
|
+
const shrinkPattern = /^([ \t]*shrinkResources\s*(?:=\s*)?)(true|false)\b/m;
|
|
765
|
+
if (shrinkPattern.test(optimizedContent)) {
|
|
766
|
+
if (!shrinkResources) optimizedContent = optimizedContent.replace(shrinkPattern, '$1false');
|
|
767
|
+
} else {
|
|
768
|
+
optimizedContent = `${newline} shrinkResources ${shrinkResources}${optimizedContent}`;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (optimizedContent !== releaseContent) {
|
|
772
|
+
writeFileSync(gradleFilePath, currentContent.slice(0, releaseBlock.start)
|
|
773
|
+
+ optimizedContent + currentContent.slice(releaseBlock.end), 'utf8');
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const propertiesPath = join(__dirname, 'android', 'gradle.properties');
|
|
777
|
+
const properties = readFileSync(propertiesPath, 'utf8');
|
|
778
|
+
const propertyPattern = /^[ \t]*[#!]?[ \t]*android\.r8\.optimizedResourceShrinking(?:[ \t]*[=:]|[ \t]+|[ \t]*$)/m;
|
|
779
|
+
const property = 'android.r8.optimizedResourceShrinking=true';
|
|
780
|
+
const nextProperties = propertyPattern.test(properties)
|
|
781
|
+
? properties
|
|
782
|
+
: `${properties.trimEnd()}${newline}${property}${newline}`;
|
|
783
|
+
if (nextProperties !== properties) writeFileSync(propertiesPath, nextProperties, 'utf8');
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function signReleaseBundleWithSha256() {
|
|
787
|
+
const buildOptions = readCapacitorConfig().android?.buildOptions;
|
|
788
|
+
if (!buildOptions?.keystorePath || !buildOptions.keystorePassword
|
|
789
|
+
|| !buildOptions.keystoreAlias || !buildOptions.keystoreAliasPassword) {
|
|
790
|
+
throw new Error('Android AAB signing requires the existing keystore path, alias and passwords.');
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const keystorePath = resolve(__dirname, 'android', buildOptions.keystorePath);
|
|
794
|
+
const bundleDirectory = join(__dirname, 'android', 'app', 'build', 'outputs', 'bundle', 'release');
|
|
795
|
+
const unsignedBundle = join(bundleDirectory, 'app-release.aab');
|
|
796
|
+
const signedBundle = join(bundleDirectory, 'app-release-signed.aab');
|
|
797
|
+
if (!existsSync(keystorePath) || !existsSync(unsignedBundle)) {
|
|
798
|
+
throw new Error('The configured upload keystore or release AAB was not found.');
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const jarsigner = process.env.JAVA_HOME
|
|
802
|
+
? join(process.env.JAVA_HOME, 'bin', process.platform === 'win32' ? 'jarsigner.exe' : 'jarsigner')
|
|
803
|
+
: 'jarsigner';
|
|
804
|
+
try {
|
|
805
|
+
// Keep passwords out of command arguments and error output.
|
|
806
|
+
execFileSync(jarsigner, [
|
|
807
|
+
'-sigalg', 'SHA256withRSA', '-digestalg', 'SHA-256',
|
|
808
|
+
'-keystore', keystorePath,
|
|
809
|
+
'-storepass:env', 'CODEPLAY_AAB_STORE_PASSWORD',
|
|
810
|
+
'-keypass:env', 'CODEPLAY_AAB_KEY_PASSWORD',
|
|
811
|
+
'-signedjar', signedBundle, unsignedBundle, buildOptions.keystoreAlias,
|
|
812
|
+
], {
|
|
813
|
+
stdio: 'pipe',
|
|
814
|
+
env: {
|
|
815
|
+
...process.env,
|
|
816
|
+
CODEPLAY_AAB_STORE_PASSWORD: buildOptions.keystorePassword,
|
|
817
|
+
CODEPLAY_AAB_KEY_PASSWORD: buildOptions.keystoreAliasPassword,
|
|
818
|
+
},
|
|
819
|
+
});
|
|
820
|
+
const verification = execFileSync(jarsigner, [
|
|
821
|
+
'-J-Duser.language=en', '-J-Duser.country=US', '-verify', signedBundle,
|
|
822
|
+
], { encoding: 'utf8', stdio: 'pipe' });
|
|
823
|
+
if (!verification.includes('jar verified.') || verification.includes('treated as unsigned')) {
|
|
824
|
+
throw new Error('Signature verification failed.');
|
|
825
|
+
}
|
|
826
|
+
} catch {
|
|
827
|
+
throw new Error('SHA-256 AAB signing or verification failed. Check the configured upload key and JDK; release stopped.');
|
|
828
|
+
}
|
|
829
|
+
console.log('✅ Release AAB signed with SHA-256 using the existing upload key and signature verified.');
|
|
830
|
+
}
|
|
831
|
+
|
|
745
832
|
const amazonWindowExtensionsDependency = 'implementation "androidx.window.extensions.core:core:1.0.0"';
|
|
746
833
|
const amazonWindowExtensionsDependencyPattern = /^[ \t]*implementation\s+["']androidx\.window\.extensions\.core:core:[^"']+["'][ \t]*(?:\r?\n)?/gm;
|
|
747
834
|
|
|
@@ -828,21 +915,23 @@ rl.question('Enter new versionCode (press enter to keep current): ', (newVersion
|
|
|
828
915
|
// Determine desired minify value
|
|
829
916
|
const desiredMinify = !_isffmpegUsed;
|
|
830
917
|
|
|
831
|
-
//
|
|
832
|
-
|
|
918
|
+
// Limit minification changes to release; debug may have its own test settings.
|
|
919
|
+
const minificationBlock = findGradleBlock(updatedGradleContent, /buildTypes\s*\{[\s\S]*?\brelease\s*/);
|
|
920
|
+
if (!minificationBlock) {
|
|
921
|
+
throw new Error('Release build configuration was not found.');
|
|
922
|
+
}
|
|
923
|
+
let minificationContent = updatedGradleContent.slice(minificationBlock.start, minificationBlock.end);
|
|
924
|
+
if (/\bminifyEnabled\s*(?:=\s*)?(true|false)\b/.test(minificationContent)) {
|
|
833
925
|
// Replace existing value with desired
|
|
834
|
-
|
|
926
|
+
minificationContent = minificationContent.replace(/\bminifyEnabled\s*(?:=\s*)?(true|false)\b/, `minifyEnabled ${desiredMinify}`);
|
|
835
927
|
console.log(`Replaced minifyEnabled with ${desiredMinify}.`);
|
|
836
|
-
} else
|
|
928
|
+
} else {
|
|
837
929
|
// Insert minifyEnabled if not present
|
|
838
|
-
|
|
839
|
-
/(buildTypes\s*{[\s\S]*?release\s*{)/,
|
|
840
|
-
`$1\n minifyEnabled ${desiredMinify}`
|
|
841
|
-
);
|
|
930
|
+
minificationContent = `\n minifyEnabled ${desiredMinify}${minificationContent}`;
|
|
842
931
|
console.log(`✅ Inserted minifyEnabled ${desiredMinify} into release block.`);
|
|
843
|
-
} else {
|
|
844
|
-
console.log('⚠️ Warning: buildTypes > release block not found. minifyEnabled was not added.');
|
|
845
932
|
}
|
|
933
|
+
updatedGradleContent = updatedGradleContent.slice(0, minificationBlock.start)
|
|
934
|
+
+ minificationContent + updatedGradleContent.slice(minificationBlock.end);
|
|
846
935
|
|
|
847
936
|
// Add native debug symbols for release AABs, but leave any existing setting untouched.
|
|
848
937
|
const releaseBlock = findGradleBlock(updatedGradleContent, /buildTypes\s*\{[\s\S]*?\brelease\s*/);
|
|
@@ -1013,11 +1102,14 @@ rl.question('Enter new versionCode (press enter to keep current): ', (newVersion
|
|
|
1013
1102
|
|
|
1014
1103
|
execSync('npx cap sync android', { stdio: 'inherit' });
|
|
1015
1104
|
|
|
1105
|
+
ensureReleaseCodeOptimization();
|
|
1106
|
+
|
|
1016
1107
|
if (buildType === "APK") {
|
|
1017
1108
|
console.log("📦 Using custom Node signing script for APK build...");
|
|
1018
1109
|
execSync('node buildCodeplay/apk-store-builder.js', { stdio: 'inherit' });
|
|
1019
1110
|
} else {
|
|
1020
1111
|
execSync(`npx cap build android --androidreleasetype=${buildType}`, { stdio: 'inherit' });
|
|
1112
|
+
signReleaseBundleWithSha256();
|
|
1021
1113
|
}
|
|
1022
1114
|
|
|
1023
1115
|
|
|
@@ -1036,11 +1128,11 @@ rl.question('Enter new versionCode (press enter to keep current): ', (newVersion
|
|
|
1036
1128
|
const checkFullPath = join("AAB", newFileName.replace(/\.aab$/, `.${newExt}`));
|
|
1037
1129
|
|
|
1038
1130
|
// Rename the output file
|
|
1039
|
-
if (existsSync(oldFilePath)) {
|
|
1040
|
-
renameSync(oldFilePath, checkFullPath);
|
|
1041
|
-
console.log(`✅ Renamed output ${newExt.toUpperCase()} file to: ${path.basename(checkFullPath)}`);
|
|
1042
|
-
syncReleaseToCodeplay(storeName, finalVersionName, finalVersionCode, new Date().toISOString().slice(0, 10));
|
|
1043
|
-
} else {
|
|
1131
|
+
if (existsSync(oldFilePath)) {
|
|
1132
|
+
renameSync(oldFilePath, checkFullPath);
|
|
1133
|
+
console.log(`✅ Renamed output ${newExt.toUpperCase()} file to: ${path.basename(checkFullPath)}`);
|
|
1134
|
+
syncReleaseToCodeplay(storeName, finalVersionName, finalVersionCode, new Date().toISOString().slice(0, 10));
|
|
1135
|
+
} else {
|
|
1044
1136
|
console.error(`❌ ${newExt.toUpperCase()} file not found after build.`);
|
|
1045
1137
|
}
|
|
1046
1138
|
|