Fix GitHub repo rename checking, allow disable to save API calls

This commit is contained in:
Imran Remtulla
2026-04-14 21:11:03 -04:00
parent 1982f39069
commit dde9653856
31 changed files with 86 additions and 18 deletions

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "الرابط لا يتطابق مع مصدر معروف",
"cantInstallOlderVersion": "لا يمكن تثبيت إصدار أقدم من التطبيق",
"appIdMismatch": "معرّف الحزمة المحملة لا يتطابق مع معرّف التطبيق الحالي",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL se ne podudara s poznatim izvorom",
"cantInstallOlderVersion": "Nije moguće instalirati stariju verziju aplikacije",
"appIdMismatch": "ID preuzetog paketa se ne podudara s postojećim ID-om aplikacije",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "L'URL no coincideix amb cap font coneguda",
"cantInstallOlderVersion": "No és possible instal·lar una versió més antiga de l'aplicació",
"appIdMismatch": "L'ID del paquet descarregat no coincideix amb l'ID de l'aplicació instal·lada",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL neodpovídá žádnému známému zdroji",
"cantInstallOlderVersion": "Nelze nainstalovat starší verzi aplikace",
"appIdMismatch": "ID staženého balíčku neodpovídá ID existující aplikace",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL'en matcher ikke en kendt kilde",
"cantInstallOlderVersion": "Kan ikke installere en ældre version af en app",
"appIdMismatch": "Hentet pakke-ID matcher ikke eksisterende app-ID",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL stimmt mit keiner bekannten Quelle überein",
"cantInstallOlderVersion": "Installation einer älteren App-Version nicht möglich",
"appIdMismatch": "Die heruntergeladene Paket-ID stimmt nicht mit der vorhandenen App-ID überein",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "La URL ne konformas al konata fonto",
"cantInstallOlderVersion": "Ne eblas instali malnovan version de la Apo",
"appIdMismatch": "La identigilo de la elŝutita pakaĵo ne konformas al la identigilo de la ekzistanta apo",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL does not match a known source",
"cantInstallOlderVersion": "Cannot install an older version of an app",
"appIdMismatch": "Downloaded package ID does not match existing app ID",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "La URL no coincide con ninguna fuente conocida",
"cantInstallOlderVersion": "No se puede instalar una versión previa de la aplicación",
"appIdMismatch": "El ID del paquete descargado no coincide con el ID de la aplicación instalada",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "آدرس اینترنتی با منبع شناخته شده مطابقت ندارد",
"cantInstallOlderVersion": "نمی توان نسخه قدیمی یک برنامه را نصب کرد",
"appIdMismatch": "شناسه بسته دانلود شده با شناسه برنامه موجود مطابقت ندارد",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "L'URL ne correspond à aucune source connue",
"cantInstallOlderVersion": "Impossible d'installer une version antérieure de l'application",
"appIdMismatch": "L'ID du paquet téléchargé ne correspond pas à l'ID de l'application existante",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "O URL non concorda cunha fonte coñecida",
"cantInstallOlderVersion": "Non se pode instalar unha versión máis antiga dunha app",
"appIdMismatch": "O ID do paquete descargado non concorda co ID dunha app existente",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "A webcím nem egyezik egyetlen ismert forrással sem",
"cantInstallOlderVersion": "Nem telepíthető egy alkalmazás régebbi verziója",
"appIdMismatch": "A letöltött csomagazonosító nem egyezik a meglévő alkalmazás azonosítójával",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL tidak sesuai dengan sumber yang diketahui",
"cantInstallOlderVersion": "Tidak dapat memasang versi aplikasi yang lebih lama",
"appIdMismatch": "ID paket yang diunduh tidak sama dengan ID aplikasi yang ada",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "L'URL non corrisponde ad alcuna fonte conosciuta",
"cantInstallOlderVersion": "Impossibile installare una versione precedente di un'app",
"appIdMismatch": "L'ID del pacchetto scaricato non corrisponde all'ID dell'app esistente",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URLが既知のソースと一致しません",
"cantInstallOlderVersion": "旧バージョンのアプリをインストールできません",
"appIdMismatch": "ダウンロードしたパッケージのIDが既存のApp IDと一致しません",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL이 알려진 소스와 일치하지 않습니다",
"cantInstallOlderVersion": "앱의 이전 버전을 설치할 수 없습니다",
"appIdMismatch": "다운로드된 패키지 ID가 기존 앱 ID와 일치하지 않습니다",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL അറിയപ്പെടുന്ന ഒരു ഉറവിടവുമായും യോജിക്കുന്നില്ല",
"cantInstallOlderVersion": "ആപ്പിന്റെ പഴയ പതിപ്പ് ഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിയില്ല.",
"appIdMismatch": "ഡൗൺലോഡ് ചെയ്ത പാക്കേജ് ഐഡി നിലവിലുള്ള ആപ്പ് ഐഡിയുമായി ചേരുന്നില്ല",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL komt niet overeen met bekende bron",
"cantInstallOlderVersion": "Kan geen oudere versie van de app installeren",
"appIdMismatch": "Gedownload pakket-ID komt niet overeen met de bestaande app-ID",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "Adres URL nie pasuje do znanego źródła",
"cantInstallOlderVersion": "Nie można zainstalować starszej wersji aplikacji",
"appIdMismatch": "ID pobranego pakietu nie zgadza się z ID zainstalowanej aplikacji",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "A URL não corresponde com nenhuma fonte conhecida",
"cantInstallOlderVersion": "Não é possível instalar uma versão mais antiga de um app",
"appIdMismatch": "O ID do pacote baixado não corresponde ao existente",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "A URL não corresponde a uma fonte conhecida",
"cantInstallOlderVersion": "Não é permitido instalar uma versão anterior de uma aplicação",
"appIdMismatch": "O ID do pacote descarregado não é igual ao ID da aplicação instalada",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL-адрес не соответствует известному источнику",
"cantInstallOlderVersion": "Невозможно установить более старую версию приложения",
"appIdMismatch": "ID загруженного пакета не совпадает с существующим ID приложения",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL matchar inte känd källa",
"cantInstallOlderVersion": "Kan inte installera en äldre version av en app",
"appIdMismatch": "Nerladdat paket-ID matchar inte nuvarande App-ID",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL, bilinen bir kaynakla eşleşmiyor",
"cantInstallOlderVersion": "Uygulamanın eski bir sürümü yüklenemez",
"appIdMismatch": "İndirilen paketin kimliği mevcut uygulama kimliği ile eşleşmiyor",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL не відповідає відомому джерелу",
"cantInstallOlderVersion": "Не можна встановити старішу версію застосунку",
"appIdMismatch": "Ідентифікатор пакета, завантажений, не відповідає ідентифікатору існуючого застосунку",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL không khớp với nguồn đã biết",
"cantInstallOlderVersion": "Không thể cài đặt phiên bản cũ hơn của Ứng dụng",
"appIdMismatch": "ID gói đã tải xuống không khớp với ID ứng dụng hiện tại",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL 不符合已知來源",
"cantInstallOlderVersion": "無法安裝舊版本的應用程式",
"appIdMismatch": "下載的套件 ID 與現有的應用程式 ID 不相符",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -5,6 +5,7 @@
"urlMatchesNoSource": "URL 与已知的来源不符",
"cantInstallOlderVersion": "无法安装旧版本的应用",
"appIdMismatch": "所下载 APK 的应用 ID 与现有应用不一致",
"repoRenamedCheck": "Check if repository was moved (increases API calls)",
"repoRenamed": "Repository moved",
"repoRenamedExplanation": "This repository was moved. Updating the URL keeps future updates working.",
"updateUrl": "Update URL",

View File

@@ -86,6 +86,11 @@ class GitHub extends AppSource {
const SizedBox(height: 4),
],
),
GeneratedFormSwitch(
'checkRepoRename',
label: tr('repoRenamedCheck'),
defaultValue: true,
),
];
additionalSourceAppSpecificSettingFormItems = [
@@ -337,32 +342,53 @@ class GitHub extends AppSource {
'${await getAPIHost(additionalSettings)}/repos${standardUrl.substring('https://${hosts[0]}'.length)}';
/// Checks if the repository has been renamed or transferred.
///
///
/// This method explicitly disables automatic redirect following to detect when
/// GitHub returns a redirect (indicating the repository has moved). A redirect
/// from the GitHub API for a repository endpoint definitively indicates that
/// the repository has been renamed or transferred to a different owner.
///
///
/// Throws [RepositoryRenamedError] if a redirect is detected.
Future<void> checkForRepositoryRename(
String standardUrl,
Map<String, dynamic> additionalSettings,
Map<String, String> sourceConfigSettingValues,
) async {
if (sourceConfigSettingValues['checkRepoRename'] == "false") {
return;
}
var uri = Uri.tryParse(standardUrl);
var host = uri?.host.toLowerCase() ?? '';
// Guard against non-GitHub URLs
if (host != hosts[0] && host != 'www.${hosts[0]}') {
return;
}
var apiUrl = await convertStandardUrlToAPIUrl(standardUrl, additionalSettings);
Response res = await sourceRequest(apiUrl, additionalSettings, followRedirects: false);
var apiUrl = await convertStandardUrlToAPIUrl(
standardUrl,
additionalSettings,
);
Response res = await sourceRequest(
apiUrl,
additionalSettings,
followRedirects: false,
);
if (res.statusCode >= 300 && res.statusCode < 400) {
String? location = res.headers[HttpHeaders.locationHeader.toLowerCase()];
if (location != null) {
location = ensureAbsoluteUrl(location, Uri.parse(apiUrl));
String newUrl = location
.replaceFirst('${await getAPIHost(additionalSettings)}/repos/', 'https://${hosts[0]}/');
throw RepositoryRenamedError(standardUrl, newUrl);
Response res2 = await sourceRequest(
location,
additionalSettings,
followRedirects: false,
);
String? newUrl;
try {
newUrl = jsonDecode(res2.body)['html_url'];
} catch (e) {
// Unexpected - ignore (keep old URL)
}
if (newUrl != null) {
throw RepositoryRenamedError(standardUrl, newUrl);
}
}
}
}
@@ -377,14 +403,17 @@ class GitHub extends AppSource {
Map<String, dynamic> additionalSettings, {
Function(Response)? onHttpErrorCode,
}) async {
await checkForRepositoryRename(standardUrl, additionalSettings);
SettingsProvider settingsProvider = SettingsProvider();
await settingsProvider.initializeSettings();
var sourceConfigSettingValues = await getSourceConfigValues(
additionalSettings,
settingsProvider,
);
await checkForRepositoryRename(
standardUrl,
additionalSettings,
sourceConfigSettingValues,
);
bool includePrereleases = additionalSettings['includePrereleases'] == true;
bool fallbackToOlderReleases =
additionalSettings['fallbackToOlderReleases'] == true;

View File

@@ -532,7 +532,10 @@ Future<List<MapEntry<String, String>>> filterApksByArch(
var abis = (await DeviceInfoPlugin().androidInfo).supportedAbis;
for (var abi in abis) {
var urls2 = apkUrls
.where((element) => RegExp('.*$abi.*', caseSensitive: false).hasMatch(element.key))
.where(
(element) =>
RegExp('.*$abi.*', caseSensitive: false).hasMatch(element.key),
)
.toList();
if (urls2.isNotEmpty && urls2.length < apkUrls.length) {
apkUrls = urls2;
@@ -830,13 +833,17 @@ abstract class AppSource {
// Previous 2 variables combined into one at runtime for convenient usage + additional processing
List<List<GeneratedFormItem>> get combinedAppSpecificSettingFormItems {
var agnosticItems = cloneFormItems(
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly);
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly,
);
final versionDetectionIdx = agnosticItems
.indexWhere((row) => row.any((item) => item.key == 'versionDetection'));
final versionDetectionIdx = agnosticItems.indexWhere(
(row) => row.any((item) => item.key == 'versionDetection'),
);
if (showReleaseDateAsVersionToggle &&
versionDetectionIdx >= 0 &&
!agnosticItems.any((row) => row.any((item) => item.key == 'releaseDateAsVersion'))) {
!agnosticItems.any(
(row) => row.any((item) => item.key == 'releaseDateAsVersion'),
)) {
agnosticItems.insert(versionDetectionIdx + 1, [
GeneratedFormSwitch(
'releaseDateAsVersion',
@@ -882,7 +889,8 @@ abstract class AppSource {
if (versionDetectionDisallowed) {
for (var item in agnosticItems.expand((row) => row)) {
if (item.key == 'versionDetection' || item.key == 'useVersionCodeAsOSVersion') {
if (item.key == 'versionDetection' ||
item.key == 'useVersionCodeAsOSVersion') {
(item as GeneratedFormSwitch).disabled = true;
(item as GeneratedFormSwitch).defaultValue = false;
}
@@ -908,7 +916,9 @@ abstract class AppSource {
var val = hostChanged && !hostIdenticalDespiteAnyChange
? additionalSettings[e.key]
: additionalSettings[e.key] ??
settingsProvider.getSettingString(e.key);
(e.runtimeType == GeneratedFormSwitch
? settingsProvider.getSettingBool(e.key).toString()
: settingsProvider.getSettingString(e.key));
if (val != null) {
results[e.key] = val;
}
@@ -1098,7 +1108,7 @@ class SourceProvider {
Tencent(),
VivoAppStore(),
RuStore(),
Apk4Free(),
Apk4Free(),
Farsroid(),
CoolApk(),
RockMods(),