All Downloads are FREE. Search and download functionalities are using the official Maven repository.

name.remal.gradle_plugins.toolkit.build_logic.helpers.gradle Maven / Gradle / Ivy

The newest version!
import static java.nio.charset.StandardCharsets.UTF_8
import static java.util.stream.Collectors.toList
import static org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE
import static org.gradle.api.attributes.Category.DOCUMENTATION
import static org.gradle.api.attributes.Category.ENFORCED_PLATFORM
import static org.gradle.api.attributes.Category.REGULAR_PLATFORM
import static org.gradle.api.attributes.Category.VERIFICATION

import groovy.json.JsonGenerator
import groovy.json.JsonSlurper
import groovy.xml.XmlSlurper
import groovy.xml.slurpersupport.GPathResult
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.security.MessageDigest
import java.time.Duration
import java.util.concurrent.Callable
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import java.util.function.Supplier
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionComparator
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser
import org.gradle.internal.resource.transport.http.HttpErrorStatusCodeException
import org.gradle.util.GradleVersion
import org.json.JSONArray
import org.json.JSONObject
import org.json.JSONTokener
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.xml.sax.EntityResolver
import org.xml.sax.InputSource
import org.xml.sax.SAXException

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

buildscript {
    dependencies {
        classpath "org.json:json:20240303"
        classpath "org.jsoup:jsoup:1.18.3"
        classpath "com.vdurmont:semver4j:3.1.0"
    }
    repositories {
        mavenCentral()
    }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

Object MUTEX = new Object[0]

project.ext.isBuildSrcProject = rootProject.name == 'buildSrc'

File rootProjectDir = project.ext.rootProjectDir = project.rootDir
if (project.isBuildSrcProject) {
    rootProjectDir = project.ext.rootProjectDir = rootProjectDir.parentFile
}

project.ext.unwrapProviders = { object ->
    while (true) {
        if (object instanceof Provider) {
            object = object.get()
        } else if (object instanceof Supplier) {
            object = object.get()
        } else if (object instanceof Callable) {
            object = object.call()
        } else {
            return object
        }
    }
}

allprojects {
    project.ext.afterEvaluateOrNow = { Closure action ->
        if (project.state.executed) {
            project.configure(project, action)
        } else {
            project.afterEvaluate(action)
        }
    }

    project.ext.withPluginAfterEvaluateOrNow = { String pluginId, Closure action ->
        project.afterEvaluateOrNow {
            project.pluginManager.withPlugin(pluginId) {
                project.configure(project, action)
            }
        }
    }
}

project.ext.startWithWord = { CharSequence untypedString, CharSequence untypedNeedle ->
    if (untypedString == null || untypedNeedle == null) {
        return false
    }
    if (untypedNeedle.length() == 0) {
        return true
    }
    if (untypedString.length() == 0) {
        return false
    }
    String string = untypedString.toString()
    String needle = untypedNeedle.toString()
    if (string == needle) {
        return true
    }
    if (string.startsWith(needle)) {
        String remaining = string.substring(needle.length())
        char firstRemainingChar = remaining.charAt(0)
        char lastNeedleChar = needle.charAt(needle.length() - 1)
        if (Character.isDigit(lastNeedleChar)) {
            return !Character.isDigit(firstRemainingChar)
        } else if (Character.isLetter(lastNeedleChar)) {
            if (!Character.isLetter(lastNeedleChar)) {
                return true
            } else if (Character.isUpperCase(firstRemainingChar) && !Character.isUpperCase(lastNeedleChar)) {
                return true
            } else if (Character.isLowerCase(firstRemainingChar) && !Character.isLowerCase(lastNeedleChar)) {
                return true
            } else {
                return false
            }
        } else {
            return Character.isLetter(firstRemainingChar)
        }
    }
    return false
}

project.ext.isInstanceOf = { Object object, String parentClassName ->
    if (object == null) {
        return false
    }

    Class parentClass = Class.forName(parentClassName, true, object.class.classLoader)
    return parentClass.isInstance(object)
}

project.ext.disableTask = { Task task ->
    task.enabled = false
    task.onlyIf { false }
    task.dependsOn = []
    Iterator registeredFileProperties = task.inputs.registeredFileProperties.iterator()
    while (registeredFileProperties.hasNext()) {
        registeredFileProperties.next()
        registeredFileProperties.remove()
    }
}

project.ext.isVerificationTask = { Task task ->
    if (task instanceof VerificationTask
        || task instanceof AbstractTestTask
        || task instanceof ValidatePlugins
        || task instanceof JacocoCoverageVerification
    ) {
        return true
    }
    return false
}


List sourceSetGetConfigurationNameMethods = SourceSet.getMethods().findAll {
    it.name.startsWith('get')
        && it.name.endsWith('ConfigurationName')
        && it.returnType == String
        && it.parameterCount == 0
        && !Modifier.isStatic(it.modifiers)
}
project.ext.getSourceSetConfigurationNames = { SourceSet sourceSet ->
    return sourceSetGetConfigurationNameMethods.collect { it.invoke(sourceSet) }
        .findAll { it != null }
        .toSet()
}

allprojects {
    project.ext.getAllSourceSetConfigurations = { SourceSet sourceSet ->
        Collection sourceSetConfigurationNames = project.getSourceSetConfigurationNames(sourceSet)
        return project.configurations.matching { sourceSetConfigurationNames.contains(it.name) }
    }.memoize()

    DomainObjectSet allSourceSetsConfigurations = project.ext.allSourceSetsConfigurations = project.objects.domainObjectSet(Configuration)
    project.pluginManager.withPlugin('java') {
        project.sourceSets.all { SourceSet sourceSet ->
            project.getAllSourceSetConfigurations(sourceSet).all { allSourceSetsConfigurations.add(it) }
        }
    }
}


project.ext.getDependencyCategory = { Object dep ->
    AttributeContainer attributes = null
    if (dep instanceof ModuleDependency) {
        attributes = dep.attributes
    } else if (dep instanceof ResolvedDependencyResult) {
        attributes = dep.resolvedVariant.attributes
    } else if (dep instanceof UnresolvedDependencyResult) {
        attributes = dep.attempted.attributes
    } else if (dep instanceof Dependency) {
        return false
    } else {
        throw new GradleException("Unsupported dependency type: ${dep.class}")
    }

    Attribute categoryAttribute = attributes.keySet().find { attr ->
        if (attr instanceof String) {
            return attr == CATEGORY_ATTRIBUTE.name
        } else {
            return attr.name == CATEGORY_ATTRIBUTE.name
        }
    }
    if (categoryAttribute == null) {
        return null;
    }

    Object value = attributes.getAttribute(categoryAttribute)
    if (value instanceof String) {
        return value
    }
    return value.name
}

project.ext.isPlatformDependency = { Object dep ->
    String category = getDependencyCategory(dep)
    return [REGULAR_PLATFORM, ENFORCED_PLATFORM].contains(category)
}

project.ext.isEnforcedPlatformDependency = { Object dep ->
    String category = getDependencyCategory(dep)
    return [ENFORCED_PLATFORM].contains(category)
}

project.ext.isDocumentationDependency = { Object dep ->
    String category = getDependencyCategory(dep)
    return [DOCUMENTATION].contains(category)
}

project.ext.isVerificationDependency = { Object dep ->
    String category = getDependencyCategory(dep)
    return [VERIFICATION].contains(category)
}

project.ext.calculateBaseJavaPackageFor = { Project project ->
    String baseJavaPackage = project.ext.find('baseJavaPackage')
    if (baseJavaPackage != null) {
        return baseJavaPackage
    }

    List baseJavaPackageTokens = "${rootProject.group}:${rootProject.name}${project.path}".split(/[.:]+/)
        .findAll { !it.isEmpty() }

    int hiddenTokenPos = baseJavaPackageTokens.findIndexOf { it.contains('--') }
    if (hiddenTokenPos >= 0) {
        baseJavaPackageTokens = baseJavaPackageTokens.subList(0, hiddenTokenPos)
    }

    for (int window = 1; window <= baseJavaPackageTokens.size() / 2; ++window) {
        int index = -1
        while (true) {
            ++index
            int start = index
            int end = start + window
            int nextStart = end
            int nextEnd = nextStart + window
            if (nextEnd > baseJavaPackageTokens.size()) {
                break
            }

            String token = baseJavaPackageTokens.subList(start, end).join('.')
            String nextToken = baseJavaPackageTokens.subList(nextStart, nextEnd).join('.')
            if (token == nextToken) {
                for (int n = 1; n <= window; ++n) {
                    baseJavaPackageTokens.remove(index)
                }
                --index
            } else if ("${token}-root" == nextToken) {
                for (int n = 1; n <= window; ++n) {
                    baseJavaPackageTokens.remove(index + 1)
                }
                --index
            } else if (token == "${nextToken}-root") {
                for (int n = 1; n <= window; ++n) {
                    baseJavaPackageTokens.remove(index)
                }
                --index
            }
        }
    }

    return baseJavaPackageTokens.join('.')
        .replaceAll(/[^\w.]+/, '_')
        .replaceAll(/_{2,}/, '_')
        .replaceAll(/_\./, '.')
        .replaceAll(/\._/, '.')
        .replaceAll(/\.{2,}/, '.')
        .replaceFirst(/^\./, '')
        .replaceFirst(/\.$/, '')
}.memoize()

allprojects {
    project.ext.calculateBaseJavaPackage = {
        return project.calculateBaseJavaPackageFor(project)
    }.memoize()

    project.ext.calculateJavaModuleName = {
        return project.findProperty('javaModuleName') ?: project.calculateBaseJavaPackage()
    }.memoize()
}


allprojects {
    Closure maybeRegisterTask = { String taskName, Closure configurer ->
        try {
            return tasks.named(taskName)
        } catch (UnknownTaskException ignore) {
            // do nothing
        }
        return tasks.register(taskName) { Task task -> configurer(task) }
    }

    Closure findSourceSet = { String sourceSetName, boolean isRequired = true ->
        if (isRequired) {
            return project.sourceSets.getByName(sourceSetName)
        } else {
            return project.sourceSets.findByName(sourceSetName)
        }
    }
    project.ext.registerResolveSourceSetCompileClasspathTask = { String sourceSetName, boolean isRequired = true ->
        String taskName = "resolve${sourceSetName.capitalize()}SourceSetCompileClasspath"
        TaskProvider taskProvider = maybeRegisterTask(taskName) { Task task ->
            task.ext.isRequired = false
            task.dependsOn(project.provider { findSourceSet(sourceSetName, task.isRequired)?.with { project.configurations[it.compileClasspathConfigurationName].allDependencies } ?: [] })
            task.doLast { findSourceSet(sourceSetName, task.isRequired)?.compileClasspath?.files }
        }
        if (isRequired) {
            taskProvider.configure { Task task ->
                task.ext.isRequired = true
            }
        }
        return taskProvider
    }
    project.ext.registerResolveSourceSetRuntimeClasspathTask = { String sourceSetName, boolean isRequired = true ->
        String taskName = "resolve${sourceSetName.capitalize()}SourceSetRuntimeClasspath"
        TaskProvider taskProvider = maybeRegisterTask(taskName) { Task task ->
            task.ext.isRequired = false
            task.dependsOn(project.provider { findSourceSet(sourceSetName, task.isRequired)?.with { project.configurations[it.runtimeClasspathConfigurationName].allDependencies } ?: [] })
            task.dependsOn(project.provider { findSourceSet(sourceSetName, task.isRequired)?.classesTaskName ?: [] })
            task.doLast { findSourceSet(sourceSetName, task.isRequired)?.runtimeClasspath?.files }
        }
        if (isRequired) {
            taskProvider.configure { Task task ->
                task.ext.isRequired = true
            }
        }
        return taskProvider
    }

    Closure findConfiguration = { String configurationName, boolean isRequired = true ->
        if (isRequired) {
            return project.configurations.getByName(configurationName)
        } else {
            return project.configurations.findByName(configurationName)
        }
    }
    project.ext.registerResolveConfigurationTask = { String configurationName, boolean isRequired = true ->
        String taskName = "resolve${configurationName.capitalize()}Configuration"
        TaskProvider taskProvider = maybeRegisterTask(taskName) { Task task ->
            task.ext.isRequired = false
            task.dependsOn(project.provider { findConfiguration(configurationName, task.isRequired)?.allDependencies ?: [] })
            task.doLast { findConfiguration(configurationName, task.isRequired)?.resolve() }
        }
        if (isRequired) {
            taskProvider.configure { Task task ->
                task.ext.isRequired = true
            }
        }
        return taskProvider
    }
}


Closure sha512hex = project.ext.sha512hex = { Object content ->
    if (content instanceof CharSequence) {
        content = content.toString().getBytes('UTF-8')
    }
    if (!(content instanceof byte[])) {
        throw new IllegalAccessException("content must be instance of CharSequence or byte[]")
    }

    MessageDigest md = MessageDigest.getInstance("SHA-512")
    byte[] hash = md.digest((byte[]) content)

    StringBuilder hex = new StringBuilder(hash.length * 2)
    for (byte b : hash) {
        hex.append(String.format("%02x", b))
    }
    return hex.toString()
}


File downloadedContentCacheDir = project.layout.buildDirectory.dir('downloaded-content-cache').get().asFile

Closure getCacheFileForDownloadedUrl = project.ext.getCacheFileForDownloadedUrl = { Object untypedUrl, String cacheFileNameSuffix = null ->
    URL url = project.uri(untypedUrl).toURL()

    if (cacheFileNameSuffix == null) {
        cacheFileNameSuffix = url.path.with { String path ->
            path = new File(path).getName()
            int dotPos = path.lastIndexOf('.')
            return dotPos >= 0 ? path.substring(dotPos) : ''
        }
    }

    return new File(downloadedContentCacheDir, sha512hex(url.toString()) + cacheFileNameSuffix)
}

Closure loadUrlContent = project.ext.loadUrlContent = { Object untypedUrl, String cacheFileNameSuffix = null ->
    synchronized (MUTEX) {
        File cacheFile = getCacheFileForDownloadedUrl(untypedUrl, cacheFileNameSuffix)
        if (cacheFile.exists()) {
            return cacheFile.bytes
        }

        URL url = project.uri(untypedUrl).toURL()
        URLConnection connection = url.openConnection()
        connection.connectTimeout = 5_000
        connection.readTimeout = 30_000
        connection.useCaches = false
        try {
            if (connection instanceof HttpURLConnection) {
                if (connection.responseCode != 200) {
                    throw new HttpErrorStatusCodeException(
                        'GET',
                        url.toString(),
                        connection.responseCode,
                        'Not Found'
                    )
                }
            }

            byte[] content = connection.inputStream.bytes
            cacheFile.parentFile.mkdirs()
            cacheFile.bytes = content
            return content

        } finally {
            if (connection instanceof HttpURLConnection) {
                connection.disconnect()
            }
        }
    }
}

Closure loadTextUrlContent = project.ext.loadTextUrlContent = { Object untypedUrl, String encoding = null, String cacheFileNameSuffix = null ->
    byte[] content = loadUrlContent(untypedUrl, cacheFileNameSuffix)
    return new String(content, encoding ?: 'UTF-8')
}

Closure loadJsonFromUrl = project.ext.loadJsonFromUrl = { Object untypedUrl, String encoding = null ->
    String content = loadTextUrlContent(untypedUrl, encoding, '.json')
    return new JSONTokener(content).nextValue()
}

Closure loadHtmlFromUrl = project.ext.loadHtmlFromUrl = { Object untypedUrl, String encoding = null ->
    String content = loadTextUrlContent(untypedUrl, encoding, '.html')
    return Jsoup.parse(content, project.uri(untypedUrl).toString())
}

Closure loadXmlFromUrl = project.ext.loadXmlFromUrl = { Object untypedUrl, String encoding = null ->
    XmlSlurper xmlParser = new XmlSlurper(false, true, true)
    xmlParser.entityResolver = new EntityResolver() {
        @Override
        InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
            new InputSource(new StringReader(""))
        }
    }
    String content = loadTextUrlContent(untypedUrl, encoding, '.xml')
    return xmlParser.parseText(content)
}

Closure> getAllGradleVersions = project.ext.getAllGradleVersions = {
    List result = []

    JSONArray versionObjects = loadJsonFromUrl('https://services.gradle.org/versions/all')
    for (JSONObject versionObject : versionObjects) {
        if (versionObject.optBoolean('snapshot')) {
            continue
        }
        if (versionObject.optBoolean('nightly')) {
            continue
        }
        if (versionObject.optBoolean('releaseNightly')) {
            continue
        }
        if (versionObject.optBoolean('broken')) {
            continue
        }
        if (!versionObject.optString('milestoneFor').isEmpty()) {
            continue
        }
        if (!versionObject.optBoolean('activeRc') && !versionObject.optString('rcFor').isEmpty()) {
            continue
        }

        String version = versionObject.getString('version')
        if (version.containsIgnoreCase('milestone')) {
            continue
        }
        if (version.startsWith('0.')) {
            continue
        }

        GradleVersion gradleVersion = GradleVersion.version(version)
        if (gradleVersion.baseVersion == gradleVersion) {
            result.add(gradleVersion)
        }
    }

    result.sort(Comparator.reverseOrder())

    return result
}.memoize()

Closure> getStableGradleVersions = project.ext.getStableGradleVersions = {
    return getAllGradleVersions().findAll { it.baseVersion == it }
}.memoize()

Closure> getMinorGradleVersions = project.ext.getMinorGradleVersions = {
    return getStableGradleVersions().findAll { it.version.count('.') == 1 }
}


Closure> getFilteredJavaRuntimeVersions = { Closure filter ->
    List result = []

    JSONArray versionObjects = loadJsonFromUrl('https://api.foojay.io/disco/v3.0/major_versions?ea=false&ga=true&discovery_scope_id=public&include_versions=false')['result']
    for (JSONObject versionObject : versionObjects) {
        if (versionObject.optBoolean('early_access_only')) {
            continue
        }
        if (versionObject.optString('release_status') == 'ea') {
            continue
        }
        if (!filter(versionObject)) {
            continue
        }

        int version = versionObject.getInt('major_version')
        if (version < 8) {
            continue
        }

        result.add(version)
    }

    result.sort(Comparator.reverseOrder())
    return result
}

Closure> getAllJavaRuntimeVersions = project.ext.getAllJavaRuntimeVersions = {
    return getFilteredJavaRuntimeVersions { true }
}.memoize()

Closure> getLtsJavaRuntimeVersions = project.ext.getLtsJavaRuntimeVersions = {
    return getFilteredJavaRuntimeVersions { JSONObject versionObject ->
        versionObject.getString('term_of_support') == 'LTS'
    }
}.memoize()


Property publishedVersionsDirRelativePathProperty = project.ext.publishedVersionsDirRelativePath = project.objects.property(String)

Closure> getPluginVersions = project.ext.getPluginVersions = { String pluginId ->
    def metadata = null
    try {
        metadata = loadXmlFromUrl("https://plugins.gradle.org/m2/${pluginId.replace('.', '/')}/${pluginId}.gradle.plugin/maven-metadata.xml")
    } catch (HttpErrorStatusCodeException e) {
        if (e.statusCode == 404) {
            return []
        }
    }

    List versions = metadata.versioning.versions.version*.text().collect()

    String publishedVersionsDirRelativePath = publishedVersionsDirRelativePathProperty.orNull
    if (publishedVersionsDirRelativePath != null) {
        File publishedVersionFile = new File(rootProjectDir, "$publishedVersionsDirRelativePath/$pluginId/${pluginId}.gradle.plugin.version")
        if (publishedVersionFile.isFile()) {
            String publishedVersion = publishedVersionFile.getText('UTF-8')
            if (!publishedVersion.isEmpty()) {
                versions.add(publishedVersion)
            }
        }
    }

    VersionParser parser = new VersionParser()
    Comparator comparator = new DefaultVersionComparator().asVersionComparator()
    versions = versions.stream()
        .map { it.trim() }
        .filter { !it.isEmpty() }
        .distinct()
        .map { parser.transform(it) }
        .sorted(comparator.reversed())
        .map { it.toString() }
        .collect(toList())

    return versions
}.memoize()

Closure getPluginLastVersion = project.ext.getPluginLastVersion = { String pluginId ->
    List versions = getPluginVersions(pluginId)
    if (!versions.isEmpty()) {
        return versions.first()
    }

    throw new IllegalStateException("No versions were found for plugin '$pluginId'")
}

Closure getPluginLastVersionWithCurrentAsFallback = project.ext.getPluginLastVersionWithCurrentAsFallback = { String pluginId ->
    List versions = getPluginVersions(pluginId)
    if (!versions.isEmpty()) {
        return versions.first()
    }

    String currentVersion = project.allprojects.stream()
        .filter { it.pluginManager.hasPlugin('maven-publish') }
        .flatMap { it.publishing.publications.withType(MavenPublication).stream() }
        .map { MavenPublication.cast(it) }
        .filter { it.groupId == pluginId && it.artifactId == "${pluginId}.gradle.plugin" }
        .map { it.version }
        .filter { it != null && !it.isEmpty() }
        .findFirst()
        .orElse(null)
    if (currentVersion != null) {
        return currentVersion;
    }

    throw new IllegalStateException("No versions were found for plugin '$pluginId'")
}


ConcurrentMap getGradleApiDependencyVersionCache = new ConcurrentHashMap<>()
allprojects {
    project.ext.getGradleApiDependencyVersion = { String notation ->
        return getGradleApiDependencyVersionCache.computeIfAbsent(notation) {
            String gradleApiVersion = project.configurations.projectDependencyConstraints
                .allDependencyConstraints
                .find { "${it.group}:${it.name}" == 'name.remal.gradle-api:gradle-api' }
                ?.version
            if (gradleApiVersion == null) {
                throw new GradleException('Unknown Gradle API version')
            }

            Configuration tempConf = project.configurations.detachedConfiguration(
                project.dependencies.create("name.remal.gradle-api:gradle-api:$gradleApiVersion")
            )

            String version = tempConf.resolvedConfiguration
                .resolvedArtifacts
                .collect { it.moduleVersion }
                .collect { it.id }
                .find { "${it.group}:*" == notation || "${it.group}:${it.name}" == notation }
                ?.version
            if (version == null) {
                throw new GradleException("Gradle API dependency not found: $notation")
            }
            return version
        }
    }
}


allprojects {
    project.ext.fatJarWithDependentTaskNames = [
        'generateJava',
        'generateResources',
        'classes',
        'jar',
        'sourcesJar',
        'javadocJar',
    ]

    Closure fatJarWithImpl = { Project otherProject ->
        configurations.compileOnly.dependencies.add(dependencies.create(otherProject))

        otherProject.tasks.withType(Javadoc).configureEach { enabled = false }

        project.fatJarWithDependentTaskNames.forEach { String taskName ->
            tasks.matching { it.name == taskName }.configureEach {
                dependsOn(otherProject.tasks.matching { it.name == taskName })
            }
        }

        sourceSets.main.allSource.srcDirs(otherProject.sourceSets.main.allSource.srcDirs)
        otherProject.sourceSets.main.output.forEach { sourceSets.main.output.dir(it) }

        tasks.withType(AbstractCopyTask).configureEach {
            filesMatching(['**/package-info.class', '**/package-info.java']) {
                duplicatesStrategy = DuplicatesStrategy.EXCLUDE
            }
        }

        tasks.withType(Javadoc).configureEach { Javadoc task ->
            task.dependsOn(project.provider { otherProject.registerResolveConfigurationTask('optionalHidden') })

            task.onlyIf {
                task.classpath += otherProject.configurations.optionalHidden
                otherProject.tasks.withType(Javadoc).forEach { Javadoc otherTask ->
                    task.classpath += otherTask.classpath
                }
                return true
            }

            Set processedPackageInfoResources = new LinkedHashSet<>()
            task.exclude { FileTreeElement element ->
                String path = element.path
                if ("/$path".endsWith('/package-info.class') || "/$path".endsWith('/package-info.java')) {
                    return !processedPackageInfoResources.add(path)
                }
            }

            task.source(project.provider {
                otherProject.tasks.withType(Javadoc)
                    .collect { it.source }
            })
        }
    }

    Set fatJarWithProjects = new LinkedHashSet<>()
    project.ext.fatJarWith = { Project otherProject ->
        if (!fatJarWithProjects.add(otherProject)) {
            return
        }
        project.withPluginAfterEvaluateOrNow('java') {
            otherProject.withPluginAfterEvaluateOrNow('java') {
                fatJarWithImpl(otherProject)
            }
        }
    }
}

Closure getClassLocationFile = project.ext.getClassLocationFile = { Class clazz ->
    URL location = clazz?.protectionDomain?.codeSource?.location
    if (location == null) {
        return null
    }

    try {
        return project.file(location)

    } catch (IllegalArgumentException ignored) {
        return null
    }
}

Closure addClassLocationFileToInputs = project.ext.addClassLocationFileToInputs = { Class clazz, TaskInputs inputs ->
    File file = getClassLocationFile(clazz)
    if (file == null) {
        // do nothing
    } else if (file.isDirectory()) {
        inputs.dir(file).optional()
    } else {
        inputs.file(file).optional()
    }
}

project.ext.sendGitHubRestApiRequest = { String uriString, String method = null, Object requestBody = null ->
    URI uri = new URI(uriString)
    if (!uri.absolute) {
        if (uri.toString().startsWith('/')) {
            uri = new URI(
                project.ext['github-api-url'].replaceFirst('/+$', '')
                    + '/'
                    + uri.toString().replaceFirst('^/+', '')
            )
        } else {
            uri = new URI(
                project.ext['repository-api-url'].replaceFirst('/+$', '')
                    + '/'
                    + uri.toString().replaceFirst('^/+', '')
            )
        }
    }

    int maxAttempts = 3
    int attempt = 0
    while (true) {
        ++attempt

        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
            .timeout(Duration.ofMinutes(1))
            .uri(uri)
            .header('Authorization', "token ${project.ext['github-actions-token']}")
            .header('Accept', 'application/vnd.github+json')
            .header('Content-Type', 'application/json;charset=UTF-8')
        if (requestBody !== null) {
            HttpRequest.BodyPublisher bodyPublisher
            if (requestBody instanceof HttpRequest.BodyPublisher) {
                bodyPublisher = requestBody
            } else {
                JsonGenerator jsonGenerator = new JsonGenerator.Options()
                    .excludeNulls()
                    .excludeFieldsByName('contentHash', 'originalClassName')
                    .build()
                String jsonContent = jsonGenerator.toJson(requestBody)
                bodyPublisher = HttpRequest.BodyPublishers.ofString(jsonContent, UTF_8)
            }

            method = method?.toUpperCase() ?: 'PUT'
            requestBuilder = requestBuilder.method(method, bodyPublisher)

        } else {
            method = method?.toUpperCase() ?: 'GET'
            requestBuilder = requestBuilder."$method"()
        }

        HttpRequest request = requestBuilder.build()
        logger.info("{}", request)

        HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString(UTF_8))
        int statusCode = response.statusCode()
        if (statusCode == 404 || statusCode == 204) {
            return null
        }

        if (statusCode >= 400) {
            if (statusCode == 429
                || statusCode >= 500
            ) {
                if (attempt < maxAttempts) {
                    Thread.sleep(5_000)
                    continue
                }
            }
        }

        String responseBodyJson = response.body()
        Object responseBody = responseBodyJson.isEmpty() ? null : new JsonSlurper().parseText(responseBodyJson)

        if (statusCode >= 400) {
            throw new HttpErrorStatusCodeException(
                method,
                uri.toString(),
                statusCode,
                responseBody.toString()
            )
        }

        return responseBody
    }
}

allprojects {
    project.ext.redefineDependencyTargetJvmVersion = { String moduleNotation, int jvmVersion ->
        allprojects {
            dependencies.components {
                withModule(moduleNotation, TargetJvmVersionRule) { params(jvmVersion) }
            }
        }
    }
}

@CacheableRule
abstract class TargetJvmVersionRule implements ComponentMetadataRule {

    final Integer jvmVersion

    @Inject
    TargetJvmVersionRule(Integer jvmVersion) {
        this.jvmVersion = jvmVersion
    }

    void execute(ComponentMetadataContext context) {
        context.details.allVariants {
            attributes {
                attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, jvmVersion)
            }
        }
    }

    @Override
    String toString() {
        return "${getClass().simpleName}[$jvmVersion]"
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy