mirror of
https://github.com/ethauvin/rife2-hello.git
synced 2025-04-25 07:27:12 -07:00
Fix reloading of templates
This commit fixes how templates were reloaded. There was a bug in the plugin which used the output of the precompiled templates for development only dependencies, instead of the templates directory. This caused the templates to be always compiled and added as a resource on runtime classpath, when we only wanted the raw templates. This commit also adds functional tests to the build logic, which can be executed by running `./gradlew build-logic:test`.
This commit is contained in:
parent
65e579966c
commit
c9f286132c
18 changed files with 542 additions and 11 deletions
|
@ -33,16 +33,14 @@ repositories {
|
|||
rife2 {
|
||||
version.set("1.4.0")
|
||||
useAgent.set(true)
|
||||
precompiledTemplateTypes.addAll(HTML)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
runtimeOnly("org.eclipse.jetty:jetty-server:11.0.13")
|
||||
runtimeOnly("org.eclipse.jetty:jetty-servlet:11.0.13")
|
||||
runtimeOnly("org.slf4j:slf4j-simple:2.0.5")
|
||||
runtimeOnly(libs.bundles.jetty)
|
||||
runtimeOnly(libs.slf4j.simple)
|
||||
|
||||
testImplementation("org.jsoup:jsoup:1.15.3")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
testImplementation(libs.jsoup)
|
||||
testImplementation(libs.junit.jupiter)
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
plugins {
|
||||
`java-gradle-plugin`
|
||||
groovy
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -8,6 +9,8 @@ repositories {
|
|||
|
||||
dependencies {
|
||||
gradleApi()
|
||||
testImplementation(libs.spock.core)
|
||||
testImplementation(gradleTestKit())
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
|
@ -18,3 +21,11 @@ gradlePlugin {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||
events = setOf(org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED, org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED, org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,9 @@
|
|||
rootProject.name = "build-logic"
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ public class Rife2Plugin implements Plugin<Project> {
|
|||
public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2";
|
||||
public static final String RIFE2_GROUP = "rife2";
|
||||
public static final String WEBAPP_SRCDIR = "src/main/webapp";
|
||||
public static final String PRECOMPILE_TEMPLATES_TASK_NAME = "precompileTemplates";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
|
@ -62,7 +63,7 @@ public class Rife2Plugin implements Plugin<Project> {
|
|||
configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration);
|
||||
|
||||
var precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath, rife2Extension);
|
||||
createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, precompileTemplates);
|
||||
createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler);
|
||||
exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates);
|
||||
configureAgent(project, plugins, rife2Extension, rife2AgentClasspath);
|
||||
TaskProvider<Jar> uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates);
|
||||
|
@ -118,14 +119,13 @@ public class Rife2Plugin implements Plugin<Project> {
|
|||
|
||||
private void createRife2DevelopmentOnlyConfiguration(Project project,
|
||||
ConfigurationContainer configurations,
|
||||
DependencyHandler dependencies,
|
||||
TaskProvider<PrecompileTemplates> precompileTemplatesTask) {
|
||||
DependencyHandler dependencies) {
|
||||
var rife2DevelopmentOnly = configurations.create("rife2DevelopmentOnly", conf -> {
|
||||
conf.setDescription("Dependencies which should only be visible when running the application in development mode (and not in tests).");
|
||||
conf.setCanBeConsumed(false);
|
||||
conf.setCanBeResolved(false);
|
||||
});
|
||||
rife2DevelopmentOnly.getDependencies().add(dependencies.create(project.files(precompileTemplatesTask)));
|
||||
rife2DevelopmentOnly.getDependencies().add(dependencies.create(project.files(DEFAULT_TEMPLATES_DIR)));
|
||||
configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(rife2DevelopmentOnly);
|
||||
}
|
||||
|
||||
|
@ -174,6 +174,7 @@ public class Rife2Plugin implements Plugin<Project> {
|
|||
rife2.getUseAgent().convention(false);
|
||||
rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass()
|
||||
.map(mainClass -> mainClass + "Uber"));
|
||||
rife2.getPrecompiledTemplateTypes().convention(Collections.singletonList(TemplateType.HTML));
|
||||
return rife2;
|
||||
}
|
||||
|
||||
|
@ -216,7 +217,7 @@ public class Rife2Plugin implements Plugin<Project> {
|
|||
private static TaskProvider<PrecompileTemplates> registerPrecompileTemplateTask(Project project,
|
||||
Configuration rife2CompilerClasspath,
|
||||
Rife2Extension rife2Extension) {
|
||||
return project.getTasks().register("precompileTemplates", PrecompileTemplates.class, task -> {
|
||||
return project.getTasks().register(PRECOMPILE_TEMPLATES_TASK_NAME, PrecompileTemplates.class, task -> {
|
||||
task.setGroup(RIFE2_GROUP);
|
||||
task.setDescription("Pre-compiles the templates.");
|
||||
task.getVerbose().convention(true);
|
||||
|
|
40
build-logic/src/test-projects/minimal/build.gradle
Normal file
40
build-logic/src/test-projects/minimal/build.gradle
Normal file
|
@ -0,0 +1,40 @@
|
|||
import com.uwyn.rife2.gradle.TemplateType.*
|
||||
|
||||
plugins {
|
||||
id("application")
|
||||
id("com.uwyn.rife2")
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName = "hello"
|
||||
version = 1.0
|
||||
group = "com.example"
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass = "hello.App"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
rife2 {
|
||||
version = "1.4.0"
|
||||
useAgent = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
runtimeOnly("org.eclipse.jetty:jetty-server:11.0.13")
|
||||
runtimeOnly("org.eclipse.jetty:jetty-servlet:11.0.13")
|
||||
runtimeOnly("org.slf4j:slf4j-simple:2.0.5")
|
||||
|
||||
testImplementation("org.jsoup:jsoup:1.15.3")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
}
|
1
build-logic/src/test-projects/minimal/settings.gradle
Normal file
1
build-logic/src/test-projects/minimal/settings.gradle
Normal file
|
@ -0,0 +1 @@
|
|||
rootProject.name = 'minimal'
|
|
@ -0,0 +1,16 @@
|
|||
package hello;
|
||||
|
||||
import rife.engine.*;
|
||||
|
||||
public class App extends Site {
|
||||
public void setup() {
|
||||
var hello = get("/hello", c -> c.print(c.template("hello")));
|
||||
get("/", c -> c.redirect(hello));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Server()
|
||||
.staticResourceBase("src/main/webapp")
|
||||
.start(new App());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package hello;
|
||||
|
||||
import rife.engine.Server;
|
||||
|
||||
public class AppUber extends App {
|
||||
public static void main(String[] args) {
|
||||
new Server()
|
||||
.staticUberJarResourceBase("webapp")
|
||||
.start(new AppUber());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
[
|
||||
{
|
||||
"name":"rife.template.html.hello",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"resources":{
|
||||
"includes":[
|
||||
{"pattern":"^webapp/.*$"}
|
||||
]
|
||||
},
|
||||
"bundles":[]
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><!--v title-->Hello<!--/v--></title>
|
||||
<link rel="stylesheet" href="{{v webapp:rootUrl/}}css/style.css?{{v context:paramRandom/}}">
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello World</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
:root {
|
||||
/* fonts */
|
||||
--main-font: sans-serif;
|
||||
|
||||
/* font sizes */
|
||||
--main-font-size: 18px;
|
||||
|
||||
/* colors */
|
||||
--main-background-color: #0d0d0d;
|
||||
--main-text-color: #d0d0d0;
|
||||
|
||||
/* margins and padding */
|
||||
--content-padding: 2em;
|
||||
}
|
||||
body {
|
||||
background: var(--main-background-color);
|
||||
font-family: var(--main-font);
|
||||
font-style: var(--main-font-size);
|
||||
color: var(--main-text-color);
|
||||
padding: var(--content-padding);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* This Java source file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
package hello;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import rife.test.MockConversation;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class AppTest {
|
||||
@Test
|
||||
void verifyRoot() {
|
||||
var m = new MockConversation(new App());
|
||||
assertEquals(m.doRequest("/").getStatus(), 302);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyHello() {
|
||||
var m = new MockConversation(new App());
|
||||
assertEquals("Hello", m.doRequest("/hello")
|
||||
.getTemplate().getValue("title"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
package com.uwyn.rife2.gradle
|
||||
|
||||
import org.gradle.testkit.runner.BuildResult
|
||||
import org.gradle.testkit.runner.GradleRunner
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import org.gradle.util.GFileUtils
|
||||
import org.gradle.util.GradleVersion
|
||||
import spock.lang.Specification
|
||||
import spock.lang.TempDir
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
abstract class AbstractFunctionalTest extends Specification {
|
||||
|
||||
private final String gradleVersion = System.getProperty("gradleVersion", GradleVersion.current().version)
|
||||
|
||||
@TempDir
|
||||
Path testDirectory
|
||||
|
||||
boolean debug
|
||||
|
||||
private StringWriter outputWriter
|
||||
private StringWriter errorOutputWriter
|
||||
private String output
|
||||
private String errorOutput
|
||||
|
||||
BuildResult result
|
||||
|
||||
Path path(String... pathElements) {
|
||||
Path cur = testDirectory
|
||||
pathElements.each {
|
||||
cur = cur.resolve(it)
|
||||
}
|
||||
cur
|
||||
}
|
||||
|
||||
File file(String... pathElements) {
|
||||
path(pathElements).toFile()
|
||||
}
|
||||
|
||||
File getGroovyBuildFile() {
|
||||
file("build.gradle")
|
||||
}
|
||||
|
||||
File getBuildFile() {
|
||||
groovyBuildFile
|
||||
}
|
||||
|
||||
File getKotlinBuildFile() {
|
||||
file("build.gradle.kts")
|
||||
}
|
||||
|
||||
File getGroovySettingsFile() {
|
||||
file("settings.gradle")
|
||||
}
|
||||
|
||||
File getKotlinSettingsFile() {
|
||||
file("settings.gradle.kts")
|
||||
}
|
||||
|
||||
File getSettingsFile() {
|
||||
groovySettingsFile
|
||||
}
|
||||
|
||||
void run(String... args) {
|
||||
try {
|
||||
result = newRunner(args)
|
||||
.build()
|
||||
} finally {
|
||||
recordOutputs()
|
||||
}
|
||||
}
|
||||
|
||||
void outputContains(String text) {
|
||||
assert output.normalize().contains(text.normalize())
|
||||
}
|
||||
|
||||
void outputDoesNotContain(String text) {
|
||||
assert !output.normalize().contains(text.normalize())
|
||||
}
|
||||
|
||||
void errorOutputContains(String text) {
|
||||
assert errorOutput.normalize().contains(text.normalize())
|
||||
}
|
||||
|
||||
void tasks(@DelegatesTo(value = TaskExecutionGraph, strategy = Closure.DELEGATE_FIRST) Closure spec) {
|
||||
def graph = new TaskExecutionGraph()
|
||||
spec.delegate = graph
|
||||
spec.resolveStrategy = Closure.DELEGATE_FIRST
|
||||
spec()
|
||||
}
|
||||
|
||||
private void recordOutputs() {
|
||||
output = outputWriter.toString()
|
||||
errorOutput = errorOutputWriter.toString()
|
||||
}
|
||||
|
||||
private GradleRunner newRunner(String... args) {
|
||||
outputWriter = new StringWriter()
|
||||
errorOutputWriter = new StringWriter()
|
||||
ArrayList<String> autoArgs = computeAutoArgs()
|
||||
def runner = GradleRunner.create()
|
||||
.forwardStdOutput(tee(new OutputStreamWriter(System.out), outputWriter))
|
||||
.forwardStdError(tee(new OutputStreamWriter(System.err), errorOutputWriter))
|
||||
.withPluginClasspath()
|
||||
.withProjectDir(testDirectory.toFile())
|
||||
.withArguments([*autoArgs, *args])
|
||||
if (gradleVersion) {
|
||||
runner.withGradleVersion(gradleVersion)
|
||||
}
|
||||
if (debug) {
|
||||
runner.withDebug(true)
|
||||
}
|
||||
runner
|
||||
}
|
||||
|
||||
private ArrayList<String> computeAutoArgs() {
|
||||
List<String> autoArgs = [
|
||||
"-s",
|
||||
"--console=verbose"
|
||||
]
|
||||
if (Boolean.getBoolean("config.cache")) {
|
||||
autoArgs << '--configuration-cache'
|
||||
}
|
||||
autoArgs
|
||||
}
|
||||
|
||||
private static Writer tee(Writer one, Writer two) {
|
||||
return TeeWriter.of(one, two)
|
||||
}
|
||||
|
||||
void fails(String... args) {
|
||||
try {
|
||||
result = newRunner(args)
|
||||
.buildAndFail()
|
||||
} finally {
|
||||
recordOutputs()
|
||||
}
|
||||
}
|
||||
|
||||
private class TaskExecutionGraph {
|
||||
void succeeded(String... tasks) {
|
||||
tasks.each { task ->
|
||||
contains(task)
|
||||
assert result.task(task).outcome == TaskOutcome.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
void failed(String... tasks) {
|
||||
tasks.each { task ->
|
||||
contains(task)
|
||||
assert result.task(task).outcome == TaskOutcome.FAILED
|
||||
}
|
||||
}
|
||||
|
||||
void skipped(String... tasks) {
|
||||
tasks.each { task ->
|
||||
contains(task)
|
||||
assert result.task(task).outcome == TaskOutcome.SKIPPED
|
||||
}
|
||||
}
|
||||
|
||||
void contains(String... tasks) {
|
||||
tasks.each { task ->
|
||||
assert result.task(task) != null: "Expected to find task $task in the graph but it was missing"
|
||||
}
|
||||
}
|
||||
|
||||
void doesNotContain(String... tasks) {
|
||||
tasks.each { task ->
|
||||
assert result.task(task) == null: "Task $task should be missing from the task graph but it was found with an outcome of ${result.task(task).outcome}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void usesProject(String name) {
|
||||
File sampleDir = new File("src/test-projects/$name")
|
||||
GFileUtils.copyDirectory(sampleDir, testDirectory.toFile())
|
||||
}
|
||||
|
||||
File file(String path) {
|
||||
new File(testDirectory.toFile(), path)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.uwyn.rife2.gradle
|
||||
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
|
||||
class PackagingTest extends AbstractFunctionalTest {
|
||||
def setup() {
|
||||
usesProject("minimal")
|
||||
}
|
||||
|
||||
def "#archive contains compiled resources"() {
|
||||
def jarFile = file(archive).toPath()
|
||||
when:
|
||||
run task
|
||||
|
||||
then: "compiles templates are found in the archive"
|
||||
tasks {
|
||||
succeeded ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}"
|
||||
}
|
||||
Files.exists(jarFile)
|
||||
try (def fs = FileSystems.newFileSystem(jarFile, [:])) {
|
||||
fs.getRootDirectories().each {
|
||||
Files.walk(it).forEach { path ->
|
||||
println path
|
||||
}
|
||||
}
|
||||
assert Files.exists(fs.getPath("/rife/template/html/hello.class"))
|
||||
}
|
||||
|
||||
where:
|
||||
task | archive
|
||||
'jar' | 'build/libs/hello-1.0.jar'
|
||||
'uberJar' | 'build/libs/hello-uber-1.0.jar'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package com.uwyn.rife2.gradle
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
@CompileStatic
|
||||
class TeeWriter extends Writer {
|
||||
private final Writer one
|
||||
private final Writer two
|
||||
|
||||
static TeeWriter of(Writer one, Writer two) {
|
||||
new TeeWriter(one, two)
|
||||
}
|
||||
|
||||
private TeeWriter(Writer one, Writer two) {
|
||||
this.one = one
|
||||
this.two = two
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(int c) throws IOException {
|
||||
try {
|
||||
one.write(c)
|
||||
} finally {
|
||||
two.write(c)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(char[] cbuf) throws IOException {
|
||||
try {
|
||||
one.write(cbuf)
|
||||
} finally {
|
||||
two.write(cbuf)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(char[] cbuf, int off, int len) throws IOException {
|
||||
try {
|
||||
one.write(cbuf, off, len)
|
||||
} finally {
|
||||
two.write(cbuf, off, len)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(String str) throws IOException {
|
||||
try {
|
||||
one.write(str)
|
||||
} finally {
|
||||
two.write(str)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(String str, int off, int len) throws IOException {
|
||||
try {
|
||||
one.write(str, off, len)
|
||||
} finally {
|
||||
two.write(str, off, len)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void flush() throws IOException {
|
||||
try {
|
||||
one.flush()
|
||||
} finally {
|
||||
two.flush()
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() throws IOException {
|
||||
try {
|
||||
one.close()
|
||||
} finally {
|
||||
two.close()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package com.uwyn.rife2.gradle
|
||||
|
||||
class TemplateCompilationTest extends AbstractFunctionalTest {
|
||||
def setup() {
|
||||
usesProject("minimal")
|
||||
}
|
||||
|
||||
def "doesn't precompile templates when calling `run`"() {
|
||||
given:
|
||||
buildFile << """
|
||||
tasks.named("run") {
|
||||
doFirst {
|
||||
throw new RuntimeException("force stop")
|
||||
}
|
||||
}
|
||||
"""
|
||||
when:
|
||||
fails 'run'
|
||||
|
||||
then: "precompile templates task must not be present in task graph"
|
||||
errorOutputContains("force stop")
|
||||
tasks {
|
||||
doesNotContain ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}"
|
||||
}
|
||||
}
|
||||
|
||||
def "`run` task classpath includes template sources"() {
|
||||
given:
|
||||
buildFile << """
|
||||
tasks.register("dumpRunClasspath") {
|
||||
doLast {
|
||||
tasks.named("run").get().classpath.files.each {
|
||||
println "Classpath entry: \$it"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
when:
|
||||
run("dumpRunClasspath")
|
||||
|
||||
then: "template sources must be present in the classpath"
|
||||
outputContains("Classpath entry: ${file("src/main/templates").absolutePath}")
|
||||
}
|
||||
|
||||
def "compiles templates when running #task"() {
|
||||
when:
|
||||
run task
|
||||
|
||||
then: "precompile templates task must be present in task graph"
|
||||
tasks {
|
||||
succeeded ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}"
|
||||
}
|
||||
|
||||
where:
|
||||
task << ['jar', 'test', 'uberJar']
|
||||
}
|
||||
}
|
17
gradle/libs.versions.toml
Normal file
17
gradle/libs.versions.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[versions]
|
||||
jetty = "11.0.13"
|
||||
jsoup = "1.15.3"
|
||||
junit-jupiter = "5.9.1"
|
||||
slf4j = "2.0.5"
|
||||
spock = "2.3-groovy-3.0"
|
||||
|
||||
[libraries]
|
||||
jetty-server = { module = "org.eclipse.jetty:jetty-server", version.ref = "jetty" }
|
||||
jetty-servlet = { module = "org.eclipse.jetty:jetty-servlet", version.ref = "jetty" }
|
||||
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
||||
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
|
||||
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||
spock-core = { module = "org.spockframework:spock-core", version.ref = "spock" }
|
||||
|
||||
[bundles]
|
||||
jetty = [ "jetty-server", "jetty-servlet" ]
|
Loading…
Add table
Add a link
Reference in a new issue