Files
paseo/packages/app/plugins/with-fdroid-autolinking.js
Anton LazarevandMohamed Boudra 43d27328e4 Fastlane metadata format for F-Droid releases (#2501)
* add f-droid metadata support

* share version code calculation with website

* preserve blockquoted sections in changelogs

* refactor(android): simplify stable F-Droid metadata

Generate store changelogs only for stable releases and keep native version-code ownership in the existing release module.

* test(android): run F-Droid changelog suite in CI

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-08-22 12:43:20 +02:00

123 lines
4.1 KiB
JavaScript

const fs = require("node:fs/promises");
const path = require("node:path");
const { withAppBuildGradle, withDangerousMod, withSettingsGradle } = require("expo/config-plugins");
const { FDROID_ABI_VERSION_CODE_SUFFIXES } = require("../native-release-version");
const EXCLUDED_ANDROID_MODULES = [
"expo-camera",
"expo-notifications",
"expo-dev-client",
"expo-dev-launcher",
"expo-dev-menu",
"expo-dev-menu-interface",
];
// Generated from the shared suffix table so the Groovy literal can never drift
// from the version codes the F-Droid changelog filenames are derived from.
const FDROID_ABI_VERSION_CODE_ENTRIES = Object.entries(FDROID_ABI_VERSION_CODE_SUFFIXES)
.map(([abi, suffix]) => ` "${abi}": ${suffix},`)
.join("\n");
const FDROID_ABI_VERSION_CODE_BLOCK = `// Paseo F-Droid single-ABI version codes
def paseoAbiVersionCodes = [
${FDROID_ABI_VERSION_CODE_ENTRIES}
]
def paseoArchitectures = (findProperty("reactNativeArchitectures") ?: "")
.toString()
.split(",")
.collect { it.trim() }
.findAll { !it.isEmpty() }
if (paseoArchitectures.size() == 1) {
def paseoAbi = paseoArchitectures[0]
def paseoAbiVersionCode = paseoAbiVersionCodes[paseoAbi]
if (paseoAbiVersionCode == null) {
throw new GradleException("Unsupported Paseo Android ABI: " + paseoAbi)
}
android.defaultConfig.versionCode = android.defaultConfig.versionCode * 10 + paseoAbiVersionCode
}
`;
function configureFdroidAppBuildGradle(contents) {
let configuredContents = contents;
if (!configuredContents.includes("dependenciesInfo {")) {
const androidBlock = "android {";
if (!configuredContents.includes(androidBlock)) {
throw new Error("Could not disable F-Droid dependency metadata in app/build.gradle");
}
configuredContents = configuredContents.replace(
androidBlock,
`${androidBlock}\n dependenciesInfo {\n includeInApk = false\n includeInBundle = false\n }`,
);
}
if (!configuredContents.includes("// Paseo F-Droid single-ABI version codes")) {
configuredContents = `${configuredContents.trimEnd()}\n\n${FDROID_ABI_VERSION_CODE_BLOCK}`;
}
return configuredContents;
}
function withFdroidAutolinking(config) {
config = withDangerousMod(config, [
"android",
async (modConfig) => {
const packageJsonPath = path.join(modConfig.modRequest.projectRoot, "package.json");
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
const expo = packageJson.expo ?? {};
const autolinking = expo.autolinking ?? {};
const android = autolinking.android ?? {};
const fdroidPackageJson = {
...packageJson,
expo: {
...expo,
autolinking: {
...autolinking,
android: {
...android,
buildFromSource: [".*"],
exclude: EXCLUDED_ANDROID_MODULES,
},
},
},
};
const overlayRoot = path.join(modConfig.modRequest.platformProjectRoot, "fdroid-autolinking");
await fs.mkdir(overlayRoot, { recursive: true });
await fs.writeFile(
path.join(overlayRoot, "package.json"),
`${JSON.stringify(fdroidPackageJson, null, 2)}\n`,
);
return modConfig;
},
]);
config = withSettingsGradle(config, (modConfig) => {
const fdroidProjectRoot =
'expoAutolinking.projectRoot = new File(rootDir, "fdroid-autolinking")';
if (modConfig.modResults.contents.includes(fdroidProjectRoot)) {
return modConfig;
}
const useExpoModules = "expoAutolinking.useExpoModules()";
if (!modConfig.modResults.contents.includes(useExpoModules)) {
throw new Error("Could not configure F-Droid Expo autolinking in settings.gradle");
}
modConfig.modResults.contents = modConfig.modResults.contents.replace(
useExpoModules,
`${fdroidProjectRoot}\n${useExpoModules}`,
);
return modConfig;
});
return withAppBuildGradle(config, (modConfig) => {
modConfig.modResults.contents = configureFdroidAppBuildGradle(modConfig.modResults.contents);
return modConfig;
});
}
module.exports = withFdroidAutolinking;