From d6e440c783a8a943a42206a1678754cb2fff6ace Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Wed, 20 May 2026 18:49:12 -0700 Subject: [PATCH] fix(flatpak): improve mirror URL strategy for offline builds (#5558) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build-logic/flatpak/README.md | 2 - .../meshtastic/flatpak/FlatpakExtension.kt | 4 - .../org/meshtastic/flatpak/FlatpakPlugin.kt | 17 ++-- .../flatpak/GenerateFlatpakSourcesTask.kt | 94 ++++++++++--------- 4 files changed, 59 insertions(+), 58 deletions(-) diff --git a/build-logic/flatpak/README.md b/build-logic/flatpak/README.md index c680b7331..c4fdbbf0d 100644 --- a/build-logic/flatpak/README.md +++ b/build-logic/flatpak/README.md @@ -44,8 +44,6 @@ All options have sensible defaults. Override as needed: ```kotlin flatpak { - // Snapshot repository to fetch maven-metadata.xml from - snapshotRepoUrl.set("https://central.sonatype.com/repository/maven-snapshots") // Task that populates the Gradle cache before scanning assembleTask.set(":desktopApp:assemble") // Custom Gradle cache directory (defaults to ~/.gradle/caches/modules-2/files-2.1) diff --git a/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/FlatpakExtension.kt b/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/FlatpakExtension.kt index 1af44e1aa..c7f9354b2 100644 --- a/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/FlatpakExtension.kt +++ b/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/FlatpakExtension.kt @@ -25,7 +25,6 @@ import org.gradle.api.provider.Property * * ```kotlin * flatpak { - * snapshotRepoUrl.set("https://central.sonatype.com/repository/maven-snapshots") * assembleTask.set(":desktopApp:assemble") * } * ``` @@ -38,9 +37,6 @@ abstract class FlatpakExtension { /** Output path for the generated flatpak-sources.json manifest. */ abstract val outputFile: RegularFileProperty - /** Base URL of the Maven snapshot repository (no trailing slash). */ - abstract val snapshotRepoUrl: Property - /** Task path to depend on, ensuring the cache is fully populated before scanning. */ abstract val assembleTask: Property } diff --git a/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/FlatpakPlugin.kt b/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/FlatpakPlugin.kt index 16b278754..aea02a96e 100644 --- a/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/FlatpakPlugin.kt +++ b/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/FlatpakPlugin.kt @@ -23,19 +23,18 @@ import java.io.File class FlatpakPlugin : Plugin { override fun apply(target: Project) { with(target) { - val extension = extensions.create("flatpak", FlatpakExtension::class.java).apply { - cacheDir.convention( - layout.dir(providers.provider { File(gradle.gradleUserHomeDir, "caches/modules-2/files-2.1") }), - ) - outputFile.convention(layout.projectDirectory.file("flatpak-sources.json")) - snapshotRepoUrl.convention("https://central.sonatype.com/repository/maven-snapshots") - assembleTask.convention(":desktopApp:assemble") - } + val extension = + extensions.create("flatpak", FlatpakExtension::class.java).apply { + cacheDir.convention( + layout.dir(providers.provider { File(gradle.gradleUserHomeDir, "caches/modules-2/files-2.1") }), + ) + outputFile.convention(layout.projectDirectory.file("flatpak-sources.json")) + assembleTask.convention(":desktopApp:assemble") + } tasks.register("generateFlatpakSourcesFromCache", GenerateFlatpakSourcesTask::class.java) { cacheDir.set(extension.cacheDir) outputFile.set(extension.outputFile) - snapshotRepoUrl.set(extension.snapshotRepoUrl) dependsOn(extension.assembleTask) } } diff --git a/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/GenerateFlatpakSourcesTask.kt b/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/GenerateFlatpakSourcesTask.kt index 86dd9cd29..ad95d5833 100644 --- a/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/GenerateFlatpakSourcesTask.kt +++ b/build-logic/flatpak/src/main/kotlin/org/meshtastic/flatpak/GenerateFlatpakSourcesTask.kt @@ -20,14 +20,12 @@ import groovy.json.JsonOutput import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.work.DisableCachingByDefault -import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import org.gradle.work.DisableCachingByDefault import org.w3c.dom.Element import org.w3c.dom.NodeList import java.io.File @@ -45,10 +43,6 @@ abstract class GenerateFlatpakSourcesTask : DefaultTask() { @get:OutputFile abstract val outputFile: RegularFileProperty - /** Base URL of the Maven snapshot repository (no trailing slash). */ - @get:Input - abstract val snapshotRepoUrl: Property - init { group = "flatpak" description = "Generates a complete flatpak-sources.json manifest from the local Gradle cache directory." @@ -78,7 +72,7 @@ abstract class GenerateFlatpakSourcesTask : DefaultTask() { val cacheFolder = cacheDir.get().asFile val outputSourcesFile = outputFile.get().asFile - val snapshotBase = snapshotRepoUrl.get() + val snapshotBase = SNAPSHOT_REPO_URL logger.lifecycle("Scanning Gradle cache directory: ${cacheFolder.absolutePath}") val allowedExtensions = setOf("jar", "aar", "pom", "module") @@ -121,32 +115,45 @@ abstract class GenerateFlatpakSourcesTask : DefaultTask() { val dest = "offline-repository/$groupPath/$name/$version" val isJitpack = group.startsWith("com.github.") + val isGoogleArtifact = + group.startsWith("androidx.") || + group.startsWith("com.google.") || + group.startsWith("com.android.") + val isGradlePlugin = group.endsWith(".gradle.plugin") || group.startsWith("org.gradle.") + val primaryUrl = when { - isSnapshot -> - "$snapshotBase/$mavenPath" - - isJitpack -> - "https://jitpack.io/$mavenPath" - - else -> - "https://repo.maven.apache.org/maven2/$mavenPath" + isSnapshot -> "$snapshotBase/$mavenPath" + isJitpack -> "https://jitpack.io/$mavenPath" + isGoogleArtifact -> "https://dl.google.com/dl/android/maven2/$mavenPath" + isGradlePlugin -> "https://plugins.gradle.org/m2/$mavenPath" + else -> "https://repo.maven.apache.org/maven2/$mavenPath" } + val mavenCentralMirrors = + listOf( + "https://repo1.maven.org/maven2/$mavenPath", + "https://maven-central.storage-download.googleapis.com/maven2/$mavenPath", + "https://maven.aliyun.com/repository/public/$mavenPath", + ) + val mirrorUrls = when { - isSnapshot -> listOf( - "https://s01.oss.sonatype.org/content/repositories/snapshots/$mavenPath", - ) + isSnapshot -> + listOf("https://s01.oss.sonatype.org/content/repositories/snapshots/$mavenPath") - isJitpack -> emptyList() + isJitpack -> + buildList { + // Many com.github.* artifacts migrated to Maven Central + add("https://repo.maven.apache.org/maven2/$mavenPath") + addAll(mavenCentralMirrors) + } else -> - listOf( - "https://dl.google.com/dl/android/maven2/$mavenPath", - "https://plugins.gradle.org/m2/$mavenPath", - "https://maven-central.storage-download.googleapis.com/maven2/$mavenPath", - ) + buildList { + add("https://repo.maven.apache.org/maven2/$mavenPath") + addAll(mavenCentralMirrors) + } } FlatpakSourceCandidate( @@ -174,13 +181,14 @@ abstract class GenerateFlatpakSourcesTask : DefaultTask() { val finalEntries = deduplicated.map { candidate -> - val entry = mutableMapOf( - "type" to "file", - "url" to candidate.primaryUrl, - "sha256" to calculateSha256(candidate.file), - "dest" to candidate.dest, - "dest-filename" to candidate.destFilename, - ) + val entry = + mutableMapOf( + "type" to "file", + "url" to candidate.primaryUrl, + "sha256" to calculateSha256(candidate.file), + "dest" to candidate.dest, + "dest-filename" to candidate.destFilename, + ) if (candidate.mirrorUrls.isNotEmpty()) { entry["mirror-urls"] = candidate.mirrorUrls } @@ -194,9 +202,9 @@ abstract class GenerateFlatpakSourcesTask : DefaultTask() { } /** - * Resolves the timestamped snapshot version by fetching maven-metadata.xml from the remote - * snapshot repository. Maven snapshot repos do not serve artifacts at the generic `-SNAPSHOT` - * filename — they require the unique timestamped coordinate (e.g. `0.2.4-20260520.043744-2`). + * Resolves the timestamped snapshot version by fetching maven-metadata.xml from the remote snapshot repository. + * Maven snapshot repos do not serve artifacts at the generic `-SNAPSHOT` filename — they require the unique + * timestamped coordinate (e.g. `0.2.4-20260520.043744-2`). */ private fun resolveSnapshotVersion( snapshotBase: String, @@ -219,9 +227,7 @@ abstract class GenerateFlatpakSourcesTask : DefaultTask() { private fun findMatchingVersion(metadata: SnapshotMetadata?, extension: String): String? { if (metadata == null) return null - return metadata.snapshotVersions - .firstOrNull { it.extension == extension && it.classifier == null } - ?.value + return metadata.snapshotVersions.firstOrNull { it.extension == extension && it.classifier == null }?.value ?: metadata.fallbackValue } @@ -232,11 +238,12 @@ abstract class GenerateFlatpakSourcesTask : DefaultTask() { connection.connectTimeout = TIMEOUT_MS connection.readTimeout = TIMEOUT_MS - val doc = connection.getInputStream().use { stream -> - val dbFactory = DocumentBuilderFactory.newInstance() - val dBuilder = dbFactory.newDocumentBuilder() - dBuilder.parse(stream) - } + val doc = + connection.getInputStream().use { stream -> + val dbFactory = DocumentBuilderFactory.newInstance() + val dBuilder = dbFactory.newDocumentBuilder() + dBuilder.parse(stream) + } doc.documentElement.normalize() val root = doc.documentElement @@ -301,5 +308,6 @@ abstract class GenerateFlatpakSourcesTask : DefaultTask() { private companion object { private const val TIMEOUT_MS = 10_000 + private const val SNAPSHOT_REPO_URL = "https://central.sonatype.com/repository/maven-snapshots" } }