mirror of
https://github.com/Boof2015/astra-mobile.git
synced 2026-08-12 05:10:52 +02:00
prep for release
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
const {
|
||||
AndroidConfig,
|
||||
withAndroidManifest,
|
||||
withAppBuildGradle,
|
||||
withProjectBuildGradle,
|
||||
withSettingsGradle,
|
||||
} = require('expo/config-plugins');
|
||||
|
||||
const SETTINGS_MARKER = '// ASTRA VENDORED KOTLIN AUDIO';
|
||||
const PROJECT_BUILD_MARKER = '// ASTRA KOTLIN AUDIO SUBSTITUTION';
|
||||
const SIGNING_MARKER = '// ASTRA RELEASE SIGNING';
|
||||
const CAMERA_FEATURE = 'android.hardware.camera';
|
||||
|
||||
const SETTINGS_BLOCK = `
|
||||
|
||||
${SETTINGS_MARKER}
|
||||
include ':kotlin-audio'
|
||||
project(':kotlin-audio').projectDir = new File(rootDir, '../vendor/kotlinaudio/kotlin-audio')
|
||||
`;
|
||||
|
||||
const PROJECT_BUILD_BLOCK = `
|
||||
|
||||
${PROJECT_BUILD_MARKER}
|
||||
subprojects {
|
||||
configurations.configureEach {
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
substitute module('com.github.doublesymmetry:kotlinaudio') using project(':kotlin-audio')
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const SIGNING_CONFIGURATION = `${SIGNING_MARKER}
|
||||
def astraReleaseStorePath = System.getenv('ASTRA_ANDROID_KEYSTORE_PATH')
|
||||
def astraReleaseStorePassword = System.getenv('ASTRA_ANDROID_KEYSTORE_PASSWORD')
|
||||
def astraReleaseKeyAlias = System.getenv('ASTRA_ANDROID_KEY_ALIAS')
|
||||
def astraReleaseKeyPassword = System.getenv('ASTRA_ANDROID_KEY_PASSWORD')
|
||||
def astraAllowInsecureReleaseSigning = (System.getenv('ASTRA_ALLOW_INSECURE_RELEASE_SIGNING') ?: 'false').toBoolean()
|
||||
def astraReleaseSigningValues = [
|
||||
astraReleaseStorePath,
|
||||
astraReleaseStorePassword,
|
||||
astraReleaseKeyAlias,
|
||||
astraReleaseKeyPassword,
|
||||
]
|
||||
def astraReleaseSigningConfigured = astraReleaseSigningValues.every { value -> value != null && !value.trim().isEmpty() }
|
||||
|
||||
gradle.taskGraph.whenReady { taskGraph ->
|
||||
def astraReleaseTaskRequested = taskGraph.allTasks.any { task -> task.name.toLowerCase().contains('release') }
|
||||
if (astraReleaseTaskRequested && !astraReleaseSigningConfigured && !astraAllowInsecureReleaseSigning) {
|
||||
throw new GradleException('Release signing is not configured. Supply the ASTRA_ANDROID_KEYSTORE_* variables, or explicitly set ASTRA_ALLOW_INSECURE_RELEASE_SIGNING=true for a non-publishable local preview.')
|
||||
}
|
||||
}
|
||||
|
||||
if (astraReleaseSigningConfigured && !file(astraReleaseStorePath).isFile()) {
|
||||
throw new GradleException('ASTRA_ANDROID_KEYSTORE_PATH does not point to a file: ' + astraReleaseStorePath)
|
||||
}
|
||||
`;
|
||||
|
||||
const RELEASE_SIGNING_CONFIG = ` if (astraReleaseSigningConfigured) {
|
||||
release {
|
||||
storeFile file(astraReleaseStorePath)
|
||||
storePassword astraReleaseStorePassword
|
||||
keyAlias astraReleaseKeyAlias
|
||||
keyPassword astraReleaseKeyPassword
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
function appendBlock(contents, marker, block) {
|
||||
return contents.includes(marker) ? contents : `${contents.trimEnd()}${block}`;
|
||||
}
|
||||
|
||||
function addReleaseSigning(contents) {
|
||||
if (contents.includes(SIGNING_MARKER)) return contents;
|
||||
|
||||
const androidAnchor = 'android {';
|
||||
const signingAnchor = ' signingConfigs {\n debug {';
|
||||
const debugSigning = 'signingConfig signingConfigs.debug';
|
||||
|
||||
if (!contents.includes(androidAnchor)) {
|
||||
throw new Error('Unable to add Astra release signing: android block was not found.');
|
||||
}
|
||||
if (!contents.includes(signingAnchor)) {
|
||||
throw new Error('Unable to add Astra release signing: signingConfigs debug block was not found.');
|
||||
}
|
||||
|
||||
let result = contents.replace(androidAnchor, `${SIGNING_CONFIGURATION}\n${androidAnchor}`);
|
||||
result = result.replace(
|
||||
signingAnchor,
|
||||
` signingConfigs {\n${RELEASE_SIGNING_CONFIG} debug {`
|
||||
);
|
||||
|
||||
const lastDebugSigning = result.lastIndexOf(debugSigning);
|
||||
if (lastDebugSigning < 0) {
|
||||
throw new Error('Unable to add Astra release signing: release signing assignment was not found.');
|
||||
}
|
||||
|
||||
return `${result.slice(0, lastDebugSigning)}signingConfig astraReleaseSigningConfigured ? signingConfigs.release : signingConfigs.debug${result.slice(lastDebugSigning + debugSigning.length)}`;
|
||||
}
|
||||
|
||||
function withVendoredKotlinAudio(config) {
|
||||
config = withSettingsGradle(config, (mod) => {
|
||||
mod.modResults.contents = appendBlock(mod.modResults.contents, SETTINGS_MARKER, SETTINGS_BLOCK);
|
||||
return mod;
|
||||
});
|
||||
|
||||
return withProjectBuildGradle(config, (mod) => {
|
||||
mod.modResults.contents = appendBlock(
|
||||
mod.modResults.contents,
|
||||
PROJECT_BUILD_MARKER,
|
||||
PROJECT_BUILD_BLOCK
|
||||
);
|
||||
return mod;
|
||||
});
|
||||
}
|
||||
|
||||
function withReleaseSigning(config) {
|
||||
return withAppBuildGradle(config, (mod) => {
|
||||
mod.modResults.contents = addReleaseSigning(mod.modResults.contents);
|
||||
return mod;
|
||||
});
|
||||
}
|
||||
|
||||
function withProfileableRelease(config) {
|
||||
return withAndroidManifest(config, (mod) => {
|
||||
AndroidConfig.Manifest.ensureToolsAvailable(mod.modResults);
|
||||
ensureOptionalCameraFeature(mod.modResults);
|
||||
const application = AndroidConfig.Manifest.getMainApplicationOrThrow(mod.modResults);
|
||||
application.profileable = [
|
||||
{
|
||||
$: {
|
||||
'android:shell': 'true',
|
||||
'tools:targetApi': '29',
|
||||
},
|
||||
},
|
||||
];
|
||||
return mod;
|
||||
});
|
||||
}
|
||||
|
||||
function ensureOptionalCameraFeature(androidManifest) {
|
||||
const manifest = androidManifest.manifest;
|
||||
const features = manifest['uses-feature'] ?? [];
|
||||
const cameraFeature = features.find(
|
||||
(feature) => feature.$?.['android:name'] === CAMERA_FEATURE
|
||||
);
|
||||
|
||||
if (cameraFeature) {
|
||||
cameraFeature.$['android:required'] = 'false';
|
||||
} else {
|
||||
features.push({
|
||||
$: {
|
||||
'android:name': CAMERA_FEATURE,
|
||||
'android:required': 'false',
|
||||
},
|
||||
});
|
||||
}
|
||||
manifest['uses-feature'] = features;
|
||||
}
|
||||
|
||||
function withAstraAndroidRelease(config) {
|
||||
config = withVendoredKotlinAudio(config);
|
||||
config = withReleaseSigning(config);
|
||||
return withProfileableRelease(config);
|
||||
}
|
||||
|
||||
module.exports = withAstraAndroidRelease;
|
||||
module.exports._internal = {
|
||||
PROJECT_BUILD_MARKER,
|
||||
SETTINGS_MARKER,
|
||||
SIGNING_MARKER,
|
||||
addReleaseSigning,
|
||||
appendBlock,
|
||||
ensureOptionalCameraFeature,
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { createRequire } from 'node:module';
|
||||
import test from 'node:test';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { _internal } = require('./withAstraAndroidRelease.js');
|
||||
|
||||
const APP_GRADLE = `apply plugin: "com.android.application"
|
||||
|
||||
android {
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('debug.keystore')
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
release {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
test('adds fail-closed release signing without changing debug signing', () => {
|
||||
const transformed = _internal.addReleaseSigning(APP_GRADLE);
|
||||
|
||||
assert.match(transformed, /ASTRA_ALLOW_INSECURE_RELEASE_SIGNING/);
|
||||
assert.match(transformed, /throw new GradleException\('Release signing is not configured/);
|
||||
assert.match(transformed, /debug \{\n signingConfig signingConfigs\.debug/);
|
||||
assert.match(
|
||||
transformed,
|
||||
/release \{\n signingConfig astraReleaseSigningConfigured \? signingConfigs\.release : signingConfigs\.debug/
|
||||
);
|
||||
});
|
||||
|
||||
test('release signing transform is idempotent', () => {
|
||||
const transformed = _internal.addReleaseSigning(APP_GRADLE);
|
||||
assert.equal(_internal.addReleaseSigning(transformed), transformed);
|
||||
assert.equal(transformed.split(_internal.SIGNING_MARKER).length - 1, 1);
|
||||
});
|
||||
|
||||
test('appendBlock adds a native block exactly once', () => {
|
||||
const once = _internal.appendBlock('base\n', _internal.SETTINGS_MARKER, `\n${_internal.SETTINGS_MARKER}\nblock\n`);
|
||||
const twice = _internal.appendBlock(once, _internal.SETTINGS_MARKER, `\n${_internal.SETTINGS_MARKER}\nblock\n`);
|
||||
|
||||
assert.equal(once, twice);
|
||||
assert.equal(once.split(_internal.SETTINGS_MARKER).length - 1, 1);
|
||||
});
|
||||
|
||||
test('marks QR scanning camera hardware as optional without duplicating it', () => {
|
||||
const manifest = { manifest: {} };
|
||||
|
||||
_internal.ensureOptionalCameraFeature(manifest);
|
||||
_internal.ensureOptionalCameraFeature(manifest);
|
||||
|
||||
assert.deepEqual(manifest.manifest['uses-feature'], [
|
||||
{
|
||||
$: {
|
||||
'android:name': 'android.hardware.camera',
|
||||
'android:required': 'false',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
Reference in New Issue
Block a user