xref: /aosp_15_r20/external/kotlinx.coroutines/buildSrc/src/main/kotlin/CacheRedirector.kt (revision 7a7160fed73afa6648ef8aa100d4a336fe921d9a)
1 import org.gradle.api.*
2 import org.gradle.api.artifacts.dsl.*
3 import org.gradle.api.artifacts.repositories.*
4 import org.gradle.api.initialization.dsl.*
5 import org.gradle.kotlin.dsl.*
6 import org.jetbrains.kotlin.gradle.targets.js.nodejs.*
7 import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.*
8 import org.jetbrains.kotlin.gradle.targets.js.yarn.*
9 import java.net.*
10 
11 /**
12  * Enabled via environment variable, so that it can be reliably accessed from any piece of the build script,
13  * including buildSrc within TeamCity CI.
14  */
15 private val cacheRedirectorEnabled = System.getenv("CACHE_REDIRECTOR")?.toBoolean() == true
16 
17 /**
18  *  The list of repositories supported by cache redirector should be synced with the list at https://cache-redirector.jetbrains.com/redirects_generated.html
19  *  To add a repository to the list create an issue in ADM project (example issue https://youtrack.jetbrains.com/issue/IJI-149)
20  */
21 private val mirroredUrls = listOf(
22     "https://cdn.azul.com/zulu/bin",
23     "https://clojars.org/repo",
24     "https://dl.google.com/android/repository",
25     "https://dl.google.com/dl/android/maven2",
26     "https://dl.google.com/dl/android/studio/ide-zips",
27     "https://dl.google.com/go",
28     "https://download.jetbrains.com",
29     "https://github.com/yarnpkg/yarn/releases/download",
30     "https://jitpack.io",
31     "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap",
32     "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev",
33     "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/eap",
34     "https://nodejs.org/dist",
35     "https://oss.sonatype.org/content/repositories/releases",
36     "https://oss.sonatype.org/content/repositories/snapshots",
37     "https://oss.sonatype.org/content/repositories/staging",
38     "https://packages.confluent.io/maven/",
39     "https://plugins.gradle.org/m2",
40     "https://plugins.jetbrains.com/maven",
41     "https://repo.grails.org/grails/core",
42     "https://repo.jenkins-ci.org/releases",
43     "https://repo.maven.apache.org/maven2",
44     "https://repo.spring.io/milestone",
45     "https://repo.typesafe.com/typesafe/ivy-releases",
46     "https://repo1.maven.org/maven2",
47     "https://services.gradle.org",
48     "https://www.exasol.com/artifactory/exasol-releases",
49     "https://www.jetbrains.com/intellij-repository/nightly",
50     "https://www.jetbrains.com/intellij-repository/releases",
51     "https://www.jetbrains.com/intellij-repository/snapshots",
52     "https://www.myget.org/F/intellij-go-snapshots/maven",
53     "https://www.myget.org/F/rd-model-snapshots/maven",
54     "https://www.myget.org/F/rd-snapshots/maven",
55     "https://www.python.org/ftp",
56 )
57 
58 private val aliases = mapOf(
59     "https://repo.maven.apache.org/maven2" to "https://repo1.maven.org/maven2" // Maven Central
60 )
61 
toCacheRedirectorUrinull62 private fun URI.toCacheRedirectorUri() = URI("https://cache-redirector.jetbrains.com/$host/$path")
63 
64 private fun URI.maybeRedirect(): URI? {
65     val url = toString().trimEnd('/')
66     val dealiasedUrl = aliases.getOrDefault(url, url)
67 
68     return if (mirroredUrls.any { dealiasedUrl.startsWith(it) }) {
69         URI(dealiasedUrl).toCacheRedirectorUri()
70     } else {
71         null
72     }
73 }
74 
isCachedOrLocalnull75 private fun URI.isCachedOrLocal() = scheme == "file" ||
76     host == "cache-redirector.jetbrains.com" ||
77     host == "teamcity.jetbrains.com" ||
78     host == "buildserver.labs.intellij.net"
79 
80 private fun Project.checkRedirectUrl(url: URI, containerName: String): URI {
81     val redirected = url.maybeRedirect()
82     if (redirected == null && !url.isCachedOrLocal()) {
83         val msg = "Repository $url in $containerName should be cached with cache-redirector"
84         val details = "Using non cached repository may lead to download failures in CI builds." +
85             " Check buildSrc/src/main/kotlin/CacheRedirector.kt for details."
86         logger.warn("WARNING - $msg\n$details")
87     }
88     return if (cacheRedirectorEnabled) redirected ?: url else url
89 }
90 
Projectnull91 private fun Project.checkRedirect(repositories: RepositoryHandler, containerName: String) {
92     if (cacheRedirectorEnabled) {
93         logger.info("Redirecting repositories for $containerName")
94     }
95     for (repository in repositories) {
96         when (repository) {
97             is MavenArtifactRepository -> repository.url = checkRedirectUrl(repository.url, containerName)
98             is IvyArtifactRepository -> repository.url = checkRedirectUrl(repository.url, containerName)
99         }
100     }
101 }
102 
Projectnull103 private fun Project.configureYarnAndNodeRedirects() {
104     if (CacheRedirector.isEnabled) {
105         val yarnRootExtension = extensions.findByType<YarnRootExtension>()
106         yarnRootExtension?.downloadBaseUrl?.let {
107             yarnRootExtension.downloadBaseUrl = CacheRedirector.maybeRedirect(it)
108         }
109 
110         val nodeJsExtension = rootProject.extensions.findByType<NodeJsRootExtension>()
111         nodeJsExtension?.nodeDownloadBaseUrl?.let {
112             nodeJsExtension.nodeDownloadBaseUrl = CacheRedirector.maybeRedirect(it)
113         }
114     }
115 }
116 
117 // Used from Groovy scripts
118 // TODO get rid of Groovy, come up with a proper convention for rootProject vs arbitrary project argument
119 object CacheRedirector {
120     /**
121      * Substitutes repositories in buildScript { } block.
122      */
123     @JvmStatic
ScriptHandlernull124     fun ScriptHandler.configureBuildScript(rootProject: Project) {
125         rootProject.checkRedirect(repositories, "${rootProject.displayName} buildscript")
126     }
127 
128     @JvmStatic
configurenull129     fun configure(project: Project) {
130         project.checkRedirect(project.repositories, project.displayName)
131     }
132 
133     /**
134      * Configures JS-specific extensions to use
135      */
136     @JvmStatic
configureJsPackageManagersnull137     fun configureJsPackageManagers(project: Project) {
138         project.configureYarnAndNodeRedirects()
139     }
140 
141     /**
142      * Temporary repositories to depend on until GC milestone 4 in KGP
143      * and stable Node release. Safe to remove when its removal does not break WASM tests.
144      */
145     @JvmStatic
configureWasmNodeRepositoriesnull146     fun configureWasmNodeRepositories(project: Project) {
147         val extension = project.extensions.findByType<NodeJsRootExtension>()
148         if (extension != null) {
149             extension.nodeVersion = "21.0.0-v8-canary202309167e82ab1fa2"
150             extension.nodeDownloadBaseUrl = "https://nodejs.org/download/v8-canary"
151         }
152 
153         project.tasks.withType<KotlinNpmInstallTask>().configureEach {
154             args.add("--ignore-engines")
155         }
156     }
157 
158     @JvmStatic
maybeRedirectnull159     fun maybeRedirect(url: String): String {
160         if (!cacheRedirectorEnabled) return url
161         return URI(url).maybeRedirect()?.toString() ?: url
162     }
163 
164     @JvmStatic
165     val isEnabled get() = cacheRedirectorEnabled
166 }
167