added jpms-check plugin

This commit is contained in:
2020-10-25 01:36:04 +02:00
parent fcb9dafac5
commit 4941481ad9
27 changed files with 1152 additions and 66 deletions

View File

@@ -1,56 +1,31 @@
repositories {
jcenter()
mavenLocal()
}
group = "net.woggioni.plugins"
version = 0.1
plugins {
`java-gradle-plugin`
`maven-publish`
id("org.jetbrains.kotlin.jvm") version("1.3.71")
id("com.gradle.plugin-publish") version("0.10.1")
id("org.jetbrains.kotlin.jvm") version "1.3.72" apply false
id("com.gradle.plugin-publish") version "0.10.1" apply false
}
configure<ExtraPropertiesExtension> {
set("junit_jupiter_version", "5.5.2")
}
allprojects {
apply<JavaLibraryPlugin>()
repositories {
mavenLocal()
mavenCentral()
jcenter()
}
group = "net.woggioni.plugins"
version = 0.1
dependencies {
dependencies {
add("testImplementation", create(group="org.junit.jupiter", name="junit-jupiter-api", version=project["version.junitJupiter"]))
add("testRuntimeOnly", create(group="org.junit.jupiter", name="junit-jupiter-engine", version=project["version.junitJupiter"]))
add("testImplementation", gradleTestKit())
}
// Align versions of all Kotlin components
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.2")
testImplementation(gradleTestKit())
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.5.2")
}
gradlePlugin {
val dependencyExportPlugin by plugins.creating {
id = "net.woggioni.plugins.dependency-export"
implementationClass = "net.woggioni.plugins.DependencyExportPlugin"
tasks.named<Test>("test") {
useJUnitPlatform()
}
}
tasks.withType<Test>().configureEach {
useJUnitPlatform()
systemProperty("plugin.build.dir", tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().first().destinationDir)
systemProperty("java.io.tmpdir", buildDir.absolutePath)
systemProperty("test.gradle.user.home", project.gradle.gradleUserHomeDir)
}
tasks.withType<Wrapper>().configureEach {
gradleVersion = "6.3"
gradleVersion = "6.6"
distributionType = Wrapper.DistributionType.ALL
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions.jvmTarget = "1.8"
}

View File

@@ -0,0 +1,7 @@
repositories {
jcenter()
}
plugins {
`kotlin-dsl`
}

View File

@@ -0,0 +1,5 @@
import org.gradle.api.Project
operator fun Project.get(key : String) : String? {
return property(key) as String?
}

View File

@@ -0,0 +1,21 @@
plugins {
`java-gradle-plugin`
`maven-publish`
id("org.jetbrains.kotlin.jvm")
id("com.gradle.plugin-publish")
}
dependencies {
// Align versions of all Kotlin components
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}
gradlePlugin {
val dependencyExportPlugin by plugins.creating {
id = "net.woggioni.plugins.dependency-export"
implementationClass = "net.woggioni.plugins.dependency.export.DependencyExportPlugin"
}
}

View File

@@ -1,8 +1,10 @@
package net.woggioni.plugins
package net.woggioni.plugins.dependency.export
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.component.ComponentIdentifier
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.artifacts.result.ResolvedComponentResult
@@ -19,6 +21,7 @@ import kotlin.reflect.KMutableProperty0
open class ExportDependenciesPluginExtension(project: Project) {
var configurationName: String = "default"
var outputFile = project.buildDir.toPath().resolve("dependencies.dot")
var showArtifacts = false
}
open class RenderDependenciesPluginExtension(project: Project) {
@@ -32,8 +35,7 @@ private class Overrider(private val properties: Map<String, Any?>, private val p
return arg
}
fun overrideProperty(
property: KMutableProperty0<String>) {
fun overrideProperty(property: KMutableProperty0<String>) {
overrideProperty(property, ::identity)
}
@@ -50,10 +52,13 @@ private class Overrider(private val properties: Map<String, Any?>, private val p
object DependencyExporter {
private fun quote(s : String) = "\"" + s + "\""
fun exportDot(project: Project, ext: ExportDependenciesPluginExtension) {
val overrider = Overrider(project.properties, "exportDependencies")
overrider.overrideProperty(ext::configurationName)
overrider.overrideProperty(ext::outputFile) { value -> Paths.get(value) }
overrider.overrideProperty(ext::showArtifacts) { value -> value.toBoolean() }
var sequence = 0
val map = HashMap<ResolvedComponentResult, Int>()
@@ -81,31 +86,73 @@ object DependencyExporter {
writer.newLine()
writer.write(" #rankdir=\"LR\";")
writer.newLine()
val artifactMap = if(ext.showArtifacts) {
requestedConfiguration.resolvedConfiguration.resolvedArtifacts.asSequence().map {
it.id.componentIdentifier to it
}.groupBy(
Pair<ComponentIdentifier, ResolvedArtifact>::first,
Pair<ComponentIdentifier, ResolvedArtifact>::second
)
} else {
null
}
for (component in resolutionResult.allComponents) {
map.computeIfAbsent(component) {
sequence++
}
val artifacts = artifactMap?.let { it[component.id] }
val (shape, color) = when (component.id) {
is ProjectComponentIdentifier -> "box" to "#88ff88"
is ModuleComponentIdentifier -> "oval" to "#ffff88"
is ProjectComponentIdentifier -> (artifacts?.let { "none" } ?: "box") to "#88ff88"
is ModuleComponentIdentifier -> (artifacts?.let { "none" } ?: "oval") to "#ffff88"
else -> throw NotImplementedError("${component.id::class}")
}
val attrs = mapOf(
"label" to component.id.displayName,
"shape" to shape,
"style" to "filled",
"fillcolor" to color
)
val componentName = component.id.displayName
val label = artifacts?.let {
val rows = it.asSequence().map { resolvedArtifact ->
val artifactDescription = sequenceOf(
"type" to resolvedArtifact.type,
"classifier" to resolvedArtifact.classifier,
"extension" to resolvedArtifact.extension.takeIf {
resolvedArtifact.extension != resolvedArtifact.type
}
).mapNotNull { pair ->
when {
pair.second == null || pair.second.isEmpty() -> null
else -> "${pair.first}: ${pair.second}"
}
}.joinToString(", ")
"<TR><TD BGCOLOR=\"lightgrey\">$artifactDescription</TD></TR>"
}.joinToString()
"""
<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="2">
<TR>
<TD>${component.id.displayName}</TD>
</TR>
$rows
</TABLE>>
""".trimIndent()
} ?: quote(componentName)
val attrs = sequenceOf(
"label" to label,
"shape" to quote(shape),
"style" to quote("filled"),
artifacts?.let { "margin" to quote(0.toString()) },
"fillcolor" to quote(color)
).mapNotNull { it }.toMap()
writer.write(" node_${map[component]} [" +
attrs.entries
.asSequence()
.map { "${it.key}=\"${it.value}\"" }.joinToString(", ") +
.map { "${it.key}=${it.value}" }.joinToString(", ") +
"];")
writer.newLine()
}
for (component in resolutionResult.allComponents) {
component.dependencies.map { dependency ->
when (dependency) {
is ResolvedDependencyResult -> dependency
@@ -163,7 +210,7 @@ class DependencyExportPlugin : Plugin<Project> {
val renderDependenciesPluginExtension = RenderDependenciesPluginExtension(project)
project.extensions.add(RenderDependenciesPluginExtension::class.java, "renderDependencies", renderDependenciesPluginExtension)
val renderDependenciesTask = project.tasks.register("renderDependencies") {
project.tasks.register("renderDependencies") {
it.dependsOn(exportDependenciesTask)
it.doLast {
DependencyRenderer.render(project, renderDependenciesPluginExtension, dependencyExportExtension.outputFile)

View File

@@ -1,7 +1,7 @@
/*
* This Kotlin source file was generated by the Gradle 'init' task.
*/
package net.woggioni.plugins
package net.woggioni.plugins.dependency.export
import org.gradle.testkit.runner.GradleRunner
import org.junit.jupiter.api.BeforeEach
@@ -12,6 +12,9 @@ import java.nio.file.Path
class DependencyExportPluginTest {
@TempDir
lateinit var testGradleHomeDir : Path
@TempDir
lateinit var testProjectDir : Path
lateinit var buildFile : Path
@@ -25,7 +28,7 @@ class DependencyExportPluginTest {
return GradleRunner.create()
.withDebug(true)
.withProjectDir(testProjectDir.toFile())
.withArguments(taskName, "-s", "--info", "-g", System.getProperty("test.gradle.user.home", "."))
.withArguments(taskName, "-s", "--info", "-g", testGradleHomeDir.toString())
.withPluginClasspath()
}
@@ -35,7 +38,7 @@ class DependencyExportPluginTest {
installResource("settings.gradle.kts", testProjectDir)
installResource("gradle.properties", testProjectDir)
val runner = getStandardGradleRunnerFor("exportDependencies")
val result = runner.build()
runner.build()
}
@Test
@@ -44,6 +47,6 @@ class DependencyExportPluginTest {
installResource("settings.gradle.kts", testProjectDir)
installResource("gradle.properties", testProjectDir)
val runner = getStandardGradleRunnerFor("exportDependencies")
val result = runner.build()
runner.build()
}
}

View File

@@ -1,4 +1,4 @@
package net.woggioni.plugins
package net.woggioni.plugins.dependency.export
import java.io.FileNotFoundException
import java.io.IOException

2
gradle.properties Normal file
View File

@@ -0,0 +1,2 @@
version.junitJupiter=5.7.0
version.junitPlatform=1.7.0

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

2
gradlew vendored
View File

@@ -82,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@@ -129,6 +130,7 @@ fi
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath

1
gradlew.bat vendored
View File

@@ -84,6 +84,7 @@ set CMD_LINE_ARGS=%*
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

93
jpms-check/README.md Normal file
View File

@@ -0,0 +1,93 @@
## Overview
This plugin will add a new task to your gradle projects, the `jpms-check` task, to help you to check how easily
whether you can start using [JPMS](http://openjdk.java.net/projects/jigsaw/) in your project and which one of your dependencies you will need to update or patch
to make them JPMS-friendly
### The *jpms-check* task
The `jpms-check` task will scan through all of your project dependencies and create a document where, for
each of them, it will be reported whether they contain a module descriptor (aka `module-info.class`) or
an `Automatic-Module-Name` entry in the manifest or if the jar is a
[Multi-Release JAR file](https://openjdk.java.net/jeps/238).
Note that artifacts that do not meet any of these requirements can still be used with JPMS,
but, using the standard module finder, the module's name will be inferred from the jar file name
(with [some tweaks](https://docs.oracle.com/javase/9/docs/api/java/lang/module/ModuleFinder.html#automatic-modules)
to try exclude the version number and the artifact classifier) which is not a very reliable option.
## Install the plugin
Checkout this project and in the root folder run
```bash
./gradlew publishToMavenLocal
```
to install it to your local machine's Maven repository.
## Add the plugin to your project
Add this plugin to you project simply adding it to your projects `build.gradle`
```groovy
plugins {
id "net.woggioni.plugins.jpms-check" version "0.1"
}
```
or to your `build.gradle.kts` if you prefer the Gradle Kotlin DSL
```kotlin
plugins {
id("net.woggioni.plugins.jpms-check") version "0.1"
}
```
You can also enable it globally on your machine just create a `~/.gradle/init.gradle` file with this content
```groovy
initscript {
repositories {
mavenLocal()
mavenCentral()
jcenter()
}
dependencies {
classpath "net.woggioni.plugins:jpms-check:0.1"
}
}
allprojects {
apply plugin: net.woggioni.plugins.jpms.check.JPMSCheckPlugin
}
```
this means that the plugin will be automatically enabled for all your Gradle projects,
note that it doesn't alter the build process in any way except for creating the
`jpms-check` task, so it is relatively safe to do so.
## Configure the plugin
The `jpms-check` will by default create a `jpms-report.html` file in the build directory of the Gradle project
where it is invoked. that is a fully self contained HTML document that can easily be opened wit ha web browser without
the need of a web server. The behaviour can be customized using some Gradle properties.
### Parameters description
- `jpms-check.outputFormat` will change the format of the generated report,
at the moment only `html` and `json` are supported (the json report contains the same data of the html report,
but instead of an HTML table, it generates an array of JSON objects containing the same data).
- `jpms-check.outputFile` will set the path and the name of the generated output file.
The default is a file in the build directory named `jpms-report.html` if the `html` format is chosen or
`jpms-report.json` otherwise.
- `jpms-check.recursive` if this property is set to `true` the plugin will recursively analyze all the subprojects
of the project where it is invoked and merge the results in a single report file (the default value of this property is
`false`)
- `jpms-check.configurationName` the name of the Gradle configuration whose dependencies will be analyzed,
it defaults to the `default` configuration.
#### Example usage
Call the `jpms-check` task to analyze all of the dependencies in the current project and all of its
subproject and create a JSON report with the result in `/tmp/report.json`
```bash
gradle -Pjpms-check.recursive=true -Pjpms-check.outputFormat=json -Pjpms-check.outputFile=/tmp/report.json jpms-check
```

View File

@@ -0,0 +1,16 @@
plugins {
`java-gradle-plugin`
`maven-publish`
groovy
id("com.gradle.plugin-publish")
}
dependencies {
}
gradlePlugin {
val dependencyExportPlugin by plugins.creating {
id = "net.woggioni.plugins.jpms-check"
implementationClass = "net.woggioni.plugins.jpms.check.JPMSCheckPlugin"
}
}

View File

@@ -0,0 +1,203 @@
package net.woggioni.plugins.jpms.check
import groovy.json.JsonBuilder
import groovy.transform.Canonical
import groovy.xml.MarkupBuilder
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.result.ResolvedArtifactResult
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util.jar.JarFile
import java.util.stream.Collectors
import java.util.zip.ZipFile
import java.util.stream.Stream
class JPMSCheckPlugin implements Plugin<Project> {
@Canonical
private class CheckResult {
ResolvedArtifactResult dep
String automaticModuleName
boolean multiReleaseJar
boolean moduleInfo
boolean getJpmsFriendly() {
return automaticModuleName != null || moduleInfo
}
@Override
boolean equals(Object other) {
if(other == null) {
return false
} else if(other.class != CheckResult.class) {
return false
} else {
return dep?.id?.componentIdentifier == other.dep?.id?.componentIdentifier
}
}
@Override
int hashCode() {
return dep.id.componentIdentifier.hashCode()
}
}
private Stream<CheckResult> computeResults(Stream<ResolvedArtifactResult> artifacts) {
return artifacts.filter { ResolvedArtifactResult res ->
res.file.exists() && res.file.name.endsWith(".jar")
}.map { resolvedArtifact ->
JarFile jarFile = new JarFile(resolvedArtifact.file).with {
if (it.isMultiRelease()) {
new JarFile(
resolvedArtifact.file,
false,
ZipFile.OPEN_READ,
Runtime.version()
)
} else {
it
}
}
String automaticModuleName = jarFile.manifest?.with {it.mainAttributes.getValue("Automatic-Module-Name") }
def moduleInfoEntry = jarFile.getJarEntry("module-info.class")
new CheckResult(
dep: resolvedArtifact,
moduleInfo: moduleInfoEntry != null,
automaticModuleName: automaticModuleName,
multiReleaseJar: jarFile.isMultiRelease()
)
}
}
private void createHtmlReport(Project project, Stream<CheckResult> checkResults, Writer writer) {
def builder = new MarkupBuilder(writer)
int friendly = 0
int total = 0
def results = checkResults.peek { CheckResult res ->
total += 1
if(res.jpmsFriendly) friendly += 1
}.collect(Collectors.toList())
builder.html {
head {
meta name: "viewport", content: "width=device-width, initial-scale=1"
getClass().classLoader.getResourceAsStream('net/woggioni/plugins/jpms/check/github-markdown.css').withReader { Reader reader ->
style reader.text
}
body {
article(class: 'markdown-body') {
h1 "Project ${project.group}:${project.name}:${project.version}", style: "text-align: center;"
div {
table {
thead {
tr {
th "JPMS friendly"
th "Not JPMS friendly", colspan: 2
th "Total", colspan: 2
}
}
tbody {
tr {
td friendly, style: "text-align: center;"
td total - friendly, style: "text-align: center;", colspan: 2
td total, style: "text-align: center;", colspan: 2
}
}
thead {
th "Name"
th "Multi-release jar"
th "Automatic-Module-Name"
th "Module descriptor"
th "JPMS friendly"
}
tbody {
results.forEach {res ->
String color = res.jpmsFriendly ? "#dfd" : "fdd"
tr(style: "background-color:$color;") {
td res.dep.id.displayName
td style: "text-align: center;", res.multiReleaseJar ? "✓" : "✕"
td style: "text-align: center;", res.automaticModuleName ?: "n/a"
td style: "text-align: center;", res.moduleInfo ? "✓" : "✕"
td style: "text-align: center;", res.jpmsFriendly ? "✓" : "✕"
}
total += 1
if(res.jpmsFriendly) friendly += 1
}
}
}
}
}
}
}
}
}
private createJsonReport(Stream<CheckResult> checkResults, Writer writer) {
def builder = new JsonBuilder()
builder (checkResults.map {
[
name: it.dep.id.componentIdentifier.displayName,
automaticModuleName: it.automaticModuleName,
isMultiReleaseJar: it.multiReleaseJar,
hasModuleInfo: it.moduleInfo,
jpmsFriendly: it.jpmsFriendly
]
}.collect(Collectors.toList()))
builder.writeTo(writer)
}
@Override
void apply(Project project) {
project.tasks.register("jpms-check") {task ->
boolean recursive = project.properties["jpms-check.recursive"]?.with(Boolean.&parseBoolean) ?: false
String cfgName = project.properties["jpms-check.configurationName"] ?: "default"
String outputFormat = project.properties["jpms-check.outputFormat"] ?: "html"
Path outputFile
switch(outputFormat) {
case "html":
outputFile = Paths.get(project.properties["jpms-check.outputFile"]) ?: Paths.get(project.buildDir.path, "jpms-report.html")
break
case "json":
outputFile = Paths.get(project.properties["jpms-check.outputFile"]) ?: Paths.get(project.buildDir.path, "jpms-report.json")
break
default:
throw new IllegalArgumentException("Unsupported output format: $outputFormat")
}
doLast {
Set<CheckResult> results = (recursive ? project.subprojects.stream() : Stream.of(project)).flatMap {
Configuration requestedConfiguration = project.configurations.find { Configuration cfg ->
cfg.canBeResolved && cfg.name == cfgName
} ?: ({
def resolvableConfigurations = "[" + project.configurations
.grep { Configuration cfg -> cfg.canBeResolved }
.collect { "'${it.name}'" }
.join(",") + "]"
throw new GradleException("Configuration '$cfgName' doesn't exist or cannot be resolved, " +
"resolvable configurations in this project are " + resolvableConfigurations)
} as Configuration)
computeResults(requestedConfiguration.incoming.artifacts.artifacts.stream())
}.collect(Collectors.toSet())
Files.createDirectories(outputFile.parent)
Files.newBufferedWriter(outputFile).withWriter {
Stream<CheckResult> resultStream = results.stream().sorted(Comparator.comparing { CheckResult res ->
res.dep.id.componentIdentifier.displayName
})
switch(outputFormat) {
case "html":
createHtmlReport(project, resultStream, it)
break
case "json":
createJsonReport(resultStream, it)
break
default:
throw new IllegalArgumentException("Unsupported output format: $outputFormat")
}
}
}
}
}
}

View File

@@ -0,0 +1,710 @@
@font-face {
font-family: octicons-link;
src: url(data:font/woff;charset=utf-8;base64,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) format('woff');
}
.markdown-body {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
line-height: 1.5;
color: #24292e;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 16px;
line-height: 1.5;
word-wrap: break-word;
}
.markdown-body .pl-c {
color: #6a737d;
}
.markdown-body .pl-c1,
.markdown-body .pl-s .pl-v {
color: #005cc5;
}
.markdown-body .pl-e,
.markdown-body .pl-en {
color: #6f42c1;
}
.markdown-body .pl-smi,
.markdown-body .pl-s .pl-s1 {
color: #24292e;
}
.markdown-body .pl-ent {
color: #22863a;
}
.markdown-body .pl-k {
color: #d73a49;
}
.markdown-body .pl-s,
.markdown-body .pl-pds,
.markdown-body .pl-s .pl-pse .pl-s1,
.markdown-body .pl-sr,
.markdown-body .pl-sr .pl-cce,
.markdown-body .pl-sr .pl-sre,
.markdown-body .pl-sr .pl-sra {
color: #032f62;
}
.markdown-body .pl-v,
.markdown-body .pl-smw {
color: #e36209;
}
.markdown-body .pl-bu {
color: #b31d28;
}
.markdown-body .pl-ii {
color: #fafbfc;
background-color: #b31d28;
}
.markdown-body .pl-c2 {
color: #fafbfc;
background-color: #d73a49;
}
.markdown-body .pl-c2::before {
content: "^M";
}
.markdown-body .pl-sr .pl-cce {
font-weight: bold;
color: #22863a;
}
.markdown-body .pl-ml {
color: #735c0f;
}
.markdown-body .pl-mh,
.markdown-body .pl-mh .pl-en,
.markdown-body .pl-ms {
font-weight: bold;
color: #005cc5;
}
.markdown-body .pl-mi {
font-style: italic;
color: #24292e;
}
.markdown-body .pl-mb {
font-weight: bold;
color: #24292e;
}
.markdown-body .pl-md {
color: #b31d28;
background-color: #ffeef0;
}
.markdown-body .pl-mi1 {
color: #22863a;
background-color: #f0fff4;
}
.markdown-body .pl-mc {
color: #e36209;
background-color: #ffebda;
}
.markdown-body .pl-mi2 {
color: #f6f8fa;
background-color: #005cc5;
}
.markdown-body .pl-mdr {
font-weight: bold;
color: #6f42c1;
}
.markdown-body .pl-ba {
color: #586069;
}
.markdown-body .pl-sg {
color: #959da5;
}
.markdown-body .pl-corl {
text-decoration: underline;
color: #032f62;
}
.markdown-body .octicon {
display: inline-block;
vertical-align: text-top;
fill: currentColor;
}
.markdown-body a {
background-color: transparent;
}
.markdown-body a:active,
.markdown-body a:hover {
outline-width: 0;
}
.markdown-body strong {
font-weight: inherit;
}
.markdown-body strong {
font-weight: bolder;
}
.markdown-body h1 {
font-size: 2em;
margin: 0.67em 0;
}
.markdown-body img {
border-style: none;
}
.markdown-body code,
.markdown-body kbd,
.markdown-body pre {
font-family: monospace, monospace;
font-size: 1em;
}
.markdown-body hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
.markdown-body input {
font: inherit;
margin: 0;
}
.markdown-body input {
overflow: visible;
}
.markdown-body [type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
.markdown-body * {
box-sizing: border-box;
}
.markdown-body input {
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
.markdown-body a {
color: #0366d6;
text-decoration: none;
}
.markdown-body a:hover {
text-decoration: underline;
}
.markdown-body strong {
font-weight: 600;
}
.markdown-body hr {
height: 0;
margin: 15px 0;
overflow: hidden;
background: transparent;
border: 0;
border-bottom: 1px solid #dfe2e5;
}
.markdown-body hr::before {
display: table;
content: "";
}
.markdown-body hr::after {
display: table;
clear: both;
content: "";
}
.markdown-body table {
border-spacing: 0;
border-collapse: collapse;
}
.markdown-body td,
.markdown-body th {
padding: 0;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
margin-top: 0;
margin-bottom: 0;
}
.markdown-body h1 {
font-size: 32px;
font-weight: 600;
}
.markdown-body h2 {
font-size: 24px;
font-weight: 600;
}
.markdown-body h3 {
font-size: 20px;
font-weight: 600;
}
.markdown-body h4 {
font-size: 16px;
font-weight: 600;
}
.markdown-body h5 {
font-size: 14px;
font-weight: 600;
}
.markdown-body h6 {
font-size: 12px;
font-weight: 600;
}
.markdown-body p {
margin-top: 0;
margin-bottom: 10px;
}
.markdown-body blockquote {
margin: 0;
}
.markdown-body ul,
.markdown-body ol {
padding-left: 0;
margin-top: 0;
margin-bottom: 0;
}
.markdown-body ol ol,
.markdown-body ul ol {
list-style-type: lower-roman;
}
.markdown-body ul ul ol,
.markdown-body ul ol ol,
.markdown-body ol ul ol,
.markdown-body ol ol ol {
list-style-type: lower-alpha;
}
.markdown-body dd {
margin-left: 0;
}
.markdown-body code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
}
.markdown-body pre {
margin-top: 0;
margin-bottom: 0;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
}
.markdown-body .octicon {
vertical-align: text-bottom;
}
.markdown-body .pl-0 {
padding-left: 0 !important;
}
.markdown-body .pl-1 {
padding-left: 4px !important;
}
.markdown-body .pl-2 {
padding-left: 8px !important;
}
.markdown-body .pl-3 {
padding-left: 16px !important;
}
.markdown-body .pl-4 {
padding-left: 24px !important;
}
.markdown-body .pl-5 {
padding-left: 32px !important;
}
.markdown-body .pl-6 {
padding-left: 40px !important;
}
.markdown-body::before {
display: table;
content: "";
}
.markdown-body::after {
display: table;
clear: both;
content: "";
}
.markdown-body>*:first-child {
margin-top: 0 !important;
}
.markdown-body>*:last-child {
margin-bottom: 0 !important;
}
.markdown-body a:not([href]) {
color: inherit;
text-decoration: none;
}
.markdown-body .anchor {
float: left;
padding-right: 4px;
margin-left: -20px;
line-height: 1;
}
.markdown-body .anchor:focus {
outline: none;
}
.markdown-body p,
.markdown-body blockquote,
.markdown-body ul,
.markdown-body ol,
.markdown-body dl,
.markdown-body table,
.markdown-body pre {
margin-top: 0;
margin-bottom: 16px;
}
.markdown-body hr {
height: 0.25em;
padding: 0;
margin: 24px 0;
background-color: #e1e4e8;
border: 0;
}
.markdown-body blockquote {
padding: 0 1em;
color: #6a737d;
border-left: 0.25em solid #dfe2e5;
}
.markdown-body blockquote>:first-child {
margin-top: 0;
}
.markdown-body blockquote>:last-child {
margin-bottom: 0;
}
.markdown-body kbd {
display: inline-block;
padding: 3px 5px;
font-size: 11px;
line-height: 10px;
color: #444d56;
vertical-align: middle;
background-color: #fafbfc;
border: solid 1px #c6cbd1;
border-bottom-color: #959da5;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #959da5;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: #1b1f23;
vertical-align: middle;
visibility: hidden;
}
.markdown-body h1:hover .anchor,
.markdown-body h2:hover .anchor,
.markdown-body h3:hover .anchor,
.markdown-body h4:hover .anchor,
.markdown-body h5:hover .anchor,
.markdown-body h6:hover .anchor {
text-decoration: none;
}
.markdown-body h1:hover .anchor .octicon-link,
.markdown-body h2:hover .anchor .octicon-link,
.markdown-body h3:hover .anchor .octicon-link,
.markdown-body h4:hover .anchor .octicon-link,
.markdown-body h5:hover .anchor .octicon-link,
.markdown-body h6:hover .anchor .octicon-link {
visibility: visible;
}
.markdown-body h1 {
padding-bottom: 0.3em;
font-size: 2em;
border-bottom: 1px solid #eaecef;
}
.markdown-body h2 {
padding-bottom: 0.3em;
font-size: 1.5em;
border-bottom: 1px solid #eaecef;
}
.markdown-body h3 {
font-size: 1.25em;
}
.markdown-body h4 {
font-size: 1em;
}
.markdown-body h5 {
font-size: 0.875em;
}
.markdown-body h6 {
font-size: 0.85em;
color: #6a737d;
}
.markdown-body ul,
.markdown-body ol {
padding-left: 2em;
}
.markdown-body ul ul,
.markdown-body ul ol,
.markdown-body ol ol,
.markdown-body ol ul {
margin-top: 0;
margin-bottom: 0;
}
.markdown-body li {
word-wrap: break-all;
}
.markdown-body li>p {
margin-top: 16px;
}
.markdown-body li+li {
margin-top: 0.25em;
}
.markdown-body dl {
padding: 0;
}
.markdown-body dl dt {
padding: 0;
margin-top: 16px;
font-size: 1em;
font-style: italic;
font-weight: 600;
}
.markdown-body dl dd {
padding: 0 16px;
margin-bottom: 16px;
}
.markdown-body table {
display: block;
width: 100%;
overflow: auto;
}
.markdown-body table th {
font-weight: 600;
width: 100%;
}
.markdown-body table th,
.markdown-body table td {
padding: 6px 13px;
border: 1px solid #dfe2e5;
}
.markdown-body table tr {
background-color: #fff;
border-top: 1px solid #c6cbd1;
}
.markdown-body table tr:nth-child(2n) {
background-color: #f6f8fa;
}
.markdown-body img {
max-width: 100%;
box-sizing: content-box;
background-color: #fff;
}
.markdown-body img[align=right] {
padding-left: 20px;
}
.markdown-body img[align=left] {
padding-right: 20px;
}
.markdown-body code {
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
background-color: rgba(27,31,35,0.05);
border-radius: 3px;
}
.markdown-body pre {
word-wrap: normal;
}
.markdown-body pre>code {
padding: 0;
margin: 0;
font-size: 100%;
word-break: normal;
white-space: pre;
background: transparent;
border: 0;
}
.markdown-body .highlight {
margin-bottom: 16px;
}
.markdown-body .highlight pre {
margin-bottom: 0;
word-break: normal;
}
.markdown-body .highlight pre,
.markdown-body pre {
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
background-color: #f6f8fa;
border-radius: 3px;
}
.markdown-body pre code {
display: inline;
max-width: auto;
padding: 0;
margin: 0;
overflow: visible;
line-height: inherit;
word-wrap: normal;
background-color: transparent;
border: 0;
}
.markdown-body .full-commit .btn-outline:not(:disabled):hover {
color: #005cc5;
border-color: #005cc5;
}
.markdown-body kbd {
display: inline-block;
padding: 3px 5px;
font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
line-height: 10px;
color: #444d56;
vertical-align: middle;
background-color: #fafbfc;
border: solid 1px #d1d5da;
border-bottom-color: #c6cbd1;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #c6cbd1;
}
.markdown-body :checked+.radio-label {
position: relative;
z-index: 1;
border-color: #0366d6;
}
.markdown-body .task-list-item {
list-style-type: none;
}
.markdown-body .task-list-item+.task-list-item {
margin-top: 3px;
}
.markdown-body .task-list-item input {
margin: 0 0.2em 0.25em -1.6em;
vertical-align: middle;
}
.markdown-body hr {
border-bottom-color: #eee;
}
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 1920px;
margin: 0 auto;
padding: 45px;
}
@media (max-width: 767px) {
.markdown-body {
padding: 15px;
}
}

View File

@@ -1 +0,0 @@
ciao

View File

@@ -7,4 +7,6 @@
* in the user manual at https://docs.gradle.org/6.1.1/userguide/multi_project_builds.html
*/
rootProject.name = "dependency-export"
rootProject.name = "my-gradle-plugins"
include("dependency-export")
include("jpms-check")