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

name.remal.gradle_plugins.toolkit.build_logic.github-submit-dependencies.gradle Maven / Gradle / Ivy

The newest version!
import static java.nio.charset.StandardCharsets.UTF_8

import groovy.json.JsonGenerator
import groovy.json.JsonOutput
import groovy.transform.EqualsAndHashCode
import groovy.transform.TupleConstructor
import java.net.http.HttpRequest

allprojects {
    pluginManager.withPlugin('java') {
        project.apply plugin: 'reporting-base'

        TaskProvider githubSubmitDependencies = tasks.register('githubSubmitDependencies') { Task task ->
            task.group = 'documentation'

            SetProperty lazyDependenciesToSubmitSet = project.objects.setProperty(DependencyToSubmit.class).with { it.finalizeValueOnRead(); it }
            task.ext.dependenciesToSubmit = lazyDependenciesToSubmitSet
            task.inputs.property('dependenciesToSubmit', lazyDependenciesToSubmitSet)

            File reportFile = project.file("${project.reporting.baseDirectory.asFile.get()}/${task.name}.json")
            task.ext.reportFile = reportFile
            task.outputs.file(reportFile).withPropertyName('reportFile')
            doFirst { reportFile.delete() }

            Closure nullIfEmpty = { String string ->
                return string != null && string.isEmpty() ? null : string
            }

            Closure nullIfJar = { String string ->
                return string != null && string == 'jar' ? null : string
            }

            Closure isExcludedCategory = { ResolvedDependencyResult resolvedDependency ->
                return isPlatformDependency(resolvedDependency) || isDocumentationDependency(resolvedDependency) || isVerificationDependency(resolvedDependency)
            }

            Collection configurationsNotToTrack = [
                'excludeFromClassesRelocation',
                'excludeFromForcedClassesRelocation',
            ]
            Closure processConfiguration = { Configuration conf, boolean isBuildscript = false ->
                lazyDependenciesToSubmitSet.addAll(provider {
                    if (configurationsNotToTrack.contains(conf.name)) {
                        return []
                    }

                    if (!conf.canSafelyBeResolved()) {
                        return []
                    }

                    Collection resolvedArtifacts = conf.resolvedConfiguration.resolvedArtifacts

                    Set result = [].toSet()
                    Closure processResolvedDependency = {
                        ResolvedDependencyResult resolvedDependency,
                        DependencyToSubmitScope scope,
                        DependencyToSubmitRelationship relationship ->
                            ModuleComponentIdentifier id = resolvedDependency.selected.id.with { id ->
                                if (id instanceof ModuleComponentIdentifier) {
                                    return id
                                } else {
                                    return null
                                }
                            }
                            if (id == null) {
                                return
                            }

                            if (isPlatformDependency(resolvedDependency)) {
                                result.add(
                                    new DependencyToSubmit(
                                        nullIfEmpty(id.group),
                                        nullIfEmpty(id.module),
                                        nullIfEmpty(id.version),
                                        null,
                                        null,
                                        scope,
                                        relationship
                                    )
                                )
                            } else {
                                Collection dependencyArtifacts = resolvedArtifacts.findAll { it.id.componentIdentifier == id }
                                dependencyArtifacts.forEach { dependencyArtifact ->
                                    result.add(
                                        new DependencyToSubmit(
                                            nullIfEmpty(id.group),
                                            nullIfEmpty(id.module),
                                            nullIfEmpty(id.version),
                                            nullIfEmpty(dependencyArtifact.classifier),
                                            nullIfEmpty(nullIfJar(dependencyArtifact.extension)),
                                            scope,
                                            relationship
                                        )
                                    )
                                }
                            }
                    }

                    DependencyToSubmitScope scope = DependencyToSubmitScope.DEVELOPMENT
                    if (isBuildscript) {
                        // do nothing
                    } else if (['runtimeClasspath', 'relocateClasses'].contains(conf.name)) {
                        scope = DependencyToSubmitScope.RUNTIME
                    }

                    conf.incoming.resolutionResult
                        .root
                        .dependencies
                        .collect { (DependencyResult) it }
                        .findAll { !it.constraint }
                        .findAll { it instanceof ResolvedDependencyResult }
                        .collect { (ResolvedDependencyResult) it }
                        .findAll { !isExcludedCategory(it) }
                        .forEach { processResolvedDependency(it, scope, DependencyToSubmitRelationship.DIRECT) }

                    conf.incoming.resolutionResult
                        .allDependencies
                        .collect { (DependencyResult) it }
                        .findAll { !it.constraint }
                        .findAll { it instanceof ResolvedDependencyResult }
                        .collect { (ResolvedDependencyResult) it }
                        .findAll { !isExcludedCategory(it) }
                        .findAll { !it.selected.selectionReason.expected }
                        .forEach { processResolvedDependency(it, scope, DependencyToSubmitRelationship.INDIRECT) }

                    return result
                })
            }

            project.buildscript.configurations.all { processConfiguration(it, true) }
            project.configurations.all { processConfiguration(it) }

            ListProperty lazyDependenciesToSubmit = project.objects.listProperty(DependencyToSubmit.class).value(provider {
                lazyDependenciesToSubmitSet.get()
                    .findAll { it.version != null && !it.version.isEmpty() }
                    .toSorted()
                    .groupBy { it.toString() }
                    .values()
                    .toList()
                    .collect { it.get(0) }
            }).with { it.finalizeValueOnRead(); it }

            String projectCorrelator = project.path
            String buildFileRelativePath = rootProject.relativePath(project.buildFile)

            Property runId = project.objects.property(String).value(provider {
                [
                    'github-actions-run',
                    project.property('github-actions-run-id'),
                    project.property('github-actions-run-attempt'),
                    project.property('github-actions-job'),
                    project.property('github-actions-job-index'),
                ].collect { it ?: '' }.join('-')
            }).with { it.finalizeValueOnRead(); it }
            Property gitSha = project.objects.property(String).value(provider { project.property('git-sha') }).with { it.finalizeValueOnRead(); it }
            Property gitRef = project.objects.property(String).value(provider { project.property('git-ref') }).with { it.finalizeValueOnRead(); it }

            JsonGenerator jsonGenerator = new JsonGenerator.Options()
                .excludeNulls()
                .excludeFieldsByName('contentHash', 'originalClassName')
                .build()

            doLast {
                Closure encodeUrlPart = { String string ->
                    if (string == null) {
                        return ''
                    }
                    return URLEncoder.encode(string, UTF_8)
                        .replace("+", "%20")
                        .replace("*", "%2A")
                        .replace("%7E", "~");
                }

                Map resolved = [:]
                lazyDependenciesToSubmit.get().forEach { DependencyToSubmit dep ->
                    String packageUrl = "pkg:maven/${encodeUrlPart(dep.group)}/${encodeUrlPart(dep.name)}@${encodeUrlPart(dep.version)}"
                    if (dep.classifier) {
                        if (!packageUrl.contains('?')) {
                            packageUrl += '?'
                        } else {
                            packageUrl += '&'
                        }
                        packageUrl += "packaging=${encodeUrlPart(dep.classifier)}"
                    }

                    resolved.put(
                        dep.toString(),
                        [
                            package_url: packageUrl,
                            relationship: dep.relationship?.name()?.toLowerCase(),
                            scope: dep.scope?.name()?.toLowerCase(),
                        ]
                    )
                }

                Map manifests = [:]
                manifests.put(
                    buildFileRelativePath,
                    [
                        name: buildFileRelativePath,
                        file: [
                            source_location: buildFileRelativePath,
                        ],
                        resolved: resolved,
                    ]
                )

                Map requestBody = [
                    version: System.currentTimeMillis(),
                    job: [
                        id: runId.get(),
                        correlator: projectCorrelator,
                    ],
                    sha: gitSha.get(),
                    ref: gitRef.get(),
                    detector: [
                        name: 'name.remal.gradle-plugins.toolkit',
                        version: '0.69.7',
                        url: 'https://github.com/remal-gradle-plugins/toolkit',
                    ],
                    manifests: manifests,
                    scanned: new Date(),
                ]
                String jsonContent = jsonGenerator.toJson(requestBody)


                reportFile.parentFile.mkdirs()
                reportFile.setText(JsonOutput.prettyPrint(jsonContent), 'UTF-8')


                Map responseJson = sendGitHubRestApiRequest(
                    "dependency-graph/snapshots",
                    'POST',
                    HttpRequest.BodyPublishers.ofString(jsonContent, UTF_8)
                )
                if (responseJson.message) {
                    println responseJson.message
                } else {
                    println responseBodyJson
                }
            }
        }

        if (project.isBuildSrcProject) {
            if (gradle.startParameter.taskNames.contains(githubSubmitDependencies.name)
                || (gradle.parent && gradle.parent.startParameter.taskNames.contains(githubSubmitDependencies.name))
            ) {
                project.rootProject.tasks.named('build') {
                    dependsOn(githubSubmitDependencies)
                }
            }
        }
    }
}

@TupleConstructor
@EqualsAndHashCode
class DependencyToSubmit implements Serializable, Comparable {

    String group
    String name
    String version
    String classifier
    String type

    DependencyToSubmitScope scope
    DependencyToSubmitRelationship relationship

    String toString() {
        String group = this.group ?: ''
        String name = this.name ?: ''
        String version = this.version ?: ''
        String classifier = this.classifier ?: ''
        String type = this.type ?: ''
        return "$group:$name:$version:$classifier@$type"
            .replaceFirst(/:?@(jar)?$/, '')
    }

    private static final Comparator scopeComparator = Comparator.nullsLast(Comparator.naturalOrder())
    private static final Comparator relationshipComparator = Comparator.nullsLast(Comparator.naturalOrder())

    @Override
    int compareTo(DependencyToSubmit other) {
        int result = toString() <=> other.toString()
        if (result == 0) {
            result = scopeComparator.compare(scope, other.scope)
        }
        if (result == 0) {
            result = relationshipComparator.compare(relationship, other.relationship)
        }
        return result
    }

}

enum DependencyToSubmitScope {
    RUNTIME,
    DEVELOPMENT,
}

enum DependencyToSubmitRelationship {
    DIRECT,
    INDIRECT,
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy