From 3a4a5a9d10b08ccf9ab8cab7fbe69261f42acb9e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 23 Dec 2020 16:33:35 +0100 Subject: [PATCH] Migrate scm-it module to gradle --- build-plugins/build.gradle | 4 + .../cloudogu/scm/IntegrationTestPlugin.groovy | 52 +++++++++ .../groovy/com/cloudogu/scm/ScmServer.groovy | 21 +++- .../cloudogu/scm/ScmServerExtension.groovy | 105 ++++++++++++++++++ .../com/cloudogu/scm/ServePlugin.groovy | 7 +- .../groovy/com/cloudogu/scm/ServeTask.groovy | 60 +++++++++- .../com/cloudogu/scm/StopScmServer.groovy | 58 ++++++++++ .../cloudogu/scm/WriteServerConfigTask.groovy | 57 ++++++---- gradle.properties | 2 +- scm-it/build.gradle | 74 ++++++++++++ scm-plugins/scm-git-plugin/build.gradle | 19 +++- scm-plugins/scm-hg-plugin/build.gradle | 16 +++ scm-plugins/scm-svn-plugin/build.gradle | 16 +++ scm-webapp/build.gradle | 6 + settings.gradle | 1 + 15 files changed, 466 insertions(+), 32 deletions(-) create mode 100644 build-plugins/src/main/groovy/com/cloudogu/scm/IntegrationTestPlugin.groovy create mode 100644 build-plugins/src/main/groovy/com/cloudogu/scm/ScmServerExtension.groovy create mode 100644 build-plugins/src/main/groovy/com/cloudogu/scm/StopScmServer.groovy create mode 100644 scm-it/build.gradle diff --git a/build-plugins/build.gradle b/build-plugins/build.gradle index 56d6b68ff2..6cae218a7d 100644 --- a/build-plugins/build.gradle +++ b/build-plugins/build.gradle @@ -51,6 +51,10 @@ gradlePlugin { id = 'org.scm-manager.serve' implementationClass = 'com.cloudogu.scm.ServePlugin' } + integrationTest { + id = 'org.scm-manager.integration-tests' + implementationClass = 'com.cloudogu.scm.IntegrationTestPlugin' + } } } diff --git a/build-plugins/src/main/groovy/com/cloudogu/scm/IntegrationTestPlugin.groovy b/build-plugins/src/main/groovy/com/cloudogu/scm/IntegrationTestPlugin.groovy new file mode 100644 index 0000000000..ac8fb8fb78 --- /dev/null +++ b/build-plugins/src/main/groovy/com/cloudogu/scm/IntegrationTestPlugin.groovy @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +package com.cloudogu.scm + +import org.gradle.api.Plugin +import org.gradle.api.Project + +class IntegrationTestPlugin implements Plugin { + + void apply(Project project) { + def extension = project.extensions.create("scmServer", ScmServerExtension, project) + + project.tasks.register('write-server-config', WriteServerConfigTask) { + it.extension = extension + } + + project.tasks.register("startScmServer", ServeTask) { + it.extension = extension + it.waitForCompletion = false + it.frontend = false + dependsOn 'write-server-config' + } + + project.tasks.register("stopScmServer", StopScmServer) { + it.extension = extension + } + } + +} diff --git a/build-plugins/src/main/groovy/com/cloudogu/scm/ScmServer.groovy b/build-plugins/src/main/groovy/com/cloudogu/scm/ScmServer.groovy index d02ed7a736..0be8f81b3c 100644 --- a/build-plugins/src/main/groovy/com/cloudogu/scm/ScmServer.groovy +++ b/build-plugins/src/main/groovy/com/cloudogu/scm/ScmServer.groovy @@ -33,6 +33,9 @@ import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.util.component.AbstractLifeCycle import org.eclipse.jetty.util.component.LifeCycle import org.eclipse.jetty.webapp.WebAppContext +import org.eclipse.jetty.server.Handler +import org.eclipse.jetty.server.handler.HandlerList +import org.eclipse.jetty.server.handler.ShutdownHandler import java.awt.Desktop @@ -61,12 +64,20 @@ public class ScmServer { info('set stage %s', configuration.stage) System.setProperty('scm.stage', configuration.stage) - info('set livereload url', configuration.livereloadUrl) - System.setProperty('sonia.scm.ui.proxy', configuration.livereloadUrl) + if (!Strings.isNullOrEmpty(configuration.livereloadUrl)) { + info('set livereload url', configuration.livereloadUrl) + System.setProperty('sonia.scm.ui.proxy', configuration.livereloadUrl) + } server = new Server() server.addConnector(createServerConnector(server)) - server.setHandler(createScmContext()) + + HandlerList handlerList = new HandlerList() + handlerList.setHandlers([ + createScmContext(), + createShutdownHandler() + ] as Handler[]) + server.setHandler(handlerList) server.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener() { @Override void lifeCycleStarted(LifeCycle event) { @@ -119,6 +130,10 @@ public class ScmServer { return warContext } + private ShutdownHandler createShutdownHandler() { + return new ShutdownHandler("_shutdown_", true, false) + } + private ServerConnector createServerConnector(Server server) throws MalformedURLException { ServerConnector connector = new ServerConnector(server) HttpConfiguration cfg = new HttpConfiguration() diff --git a/build-plugins/src/main/groovy/com/cloudogu/scm/ScmServerExtension.groovy b/build-plugins/src/main/groovy/com/cloudogu/scm/ScmServerExtension.groovy new file mode 100644 index 0000000000..9ee1f14984 --- /dev/null +++ b/build-plugins/src/main/groovy/com/cloudogu/scm/ScmServerExtension.groovy @@ -0,0 +1,105 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +package com.cloudogu.scm + + +import org.gradle.api.artifacts.Configuration +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Input +import org.gradle.api.Project + +class ScmServerExtension implements Serializable { + + private Project project + private Configuration configuration + private boolean openBrowser = true + private boolean liveReload = true + private File warFile + + ScmServerExtension(Project project) { + this.project = project + } + + @Input + public boolean isOpenBrowser() { + return openBrowser + } + + public void setOpenBrowser(boolean openBrowser) { + this.openBrowser = openBrowser + } + + @Input + public boolean isLiveReload() { + return liveReload + } + + public void setLiveReload(boolean liveReload) { + this.liveReload = liveReload + } + + @Input + public String getHome() { + if (project.hasProperty('home')) { + return project.getProperty('home') + } + return new File(project.buildDir, 'scm-home').toString() + } + + @Input + public int getPort() { + if (project.hasProperty('port')) { + return Integer.parseInt(project.getProperty('port')) + } + return 8081 + } + + @Optional + @InputFile + public File getWarFile() { + return warFile + } + + public void setWarFile(File warFile) { + this.warFile = warFile + } + + @Optional + @Classpath + Configuration getConfiguration() { + return configuration + } + + void setConfiguration(Configuration configuration) { + this.configuration = configuration + } + + void configuration(String configuration) { + setConfiguration(project.configurations.getByName(configuration)) + } +} diff --git a/build-plugins/src/main/groovy/com/cloudogu/scm/ServePlugin.groovy b/build-plugins/src/main/groovy/com/cloudogu/scm/ServePlugin.groovy index 96efa01d08..01bba209ee 100644 --- a/build-plugins/src/main/groovy/com/cloudogu/scm/ServePlugin.groovy +++ b/build-plugins/src/main/groovy/com/cloudogu/scm/ServePlugin.groovy @@ -33,6 +33,8 @@ import com.moowork.gradle.node.NodeExtension class ServePlugin implements Plugin { void apply(Project project) { + def extension = project.extensions.create("scmServer", ScmServerExtension, project) + project.plugins.apply("com.github.node-gradle.node") def nodeExt = NodeExtension.get(project) nodeExt.setDownload(true) @@ -40,8 +42,11 @@ class ServePlugin implements Plugin { nodeExt.setYarnVersion('1.22.5') nodeExt.setNodeModulesDir( project.rootProject.projectDir ) - project.tasks.register('write-server-config', WriteServerConfigTask) + project.tasks.register('write-server-config', WriteServerConfigTask) { + it.extension = extension + } project.tasks.register("serve", ServeTask) { + it.extension = extension dependsOn 'write-server-config', 'yarnSetup' } } diff --git a/build-plugins/src/main/groovy/com/cloudogu/scm/ServeTask.groovy b/build-plugins/src/main/groovy/com/cloudogu/scm/ServeTask.groovy index 857e9bd2c6..53a52e7f01 100644 --- a/build-plugins/src/main/groovy/com/cloudogu/scm/ServeTask.groovy +++ b/build-plugins/src/main/groovy/com/cloudogu/scm/ServeTask.groovy @@ -29,18 +29,72 @@ import com.moowork.gradle.node.yarn.YarnTask import org.gradle.api.DefaultTask import org.gradle.api.tasks.JavaExec import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.Input import org.gradle.api.tasks.TaskAction +import org.gradle.api.GradleException class ServeTask extends DefaultTask { + private boolean frontend = true + private boolean waitForCompletion = true + private ScmServerExtension extension + + @Nested + ScmServerExtension getExtension() { + return extension + } + + void setExtension(ScmServerExtension extension) { + this.extension = extension + } + + @Input + boolean isFrontend() { + return frontend + } + + void setFrontend(boolean frontend) { + this.frontend = frontend + } + + @Input + boolean isWaitForCompletion() { + return waitForCompletion + } + + void setWaitForCompletion(boolean waitForCompletion) { + this.waitForCompletion = waitForCompletion + } + @TaskAction void exec() { List> actions = new ArrayList<>(); actions.add(createBackend()) - actions.add(createFrontend()) - + if (frontend) { + actions.add(createFrontend()) + } def threads = start(actions) - wait(threads) + if (waitForCompletion) { + wait(threads) + } else { + waitForPortToBeOpen() + } + } + + private void waitForPortToBeOpen() { + int retries = 180 + for (int i=0; i threads) { diff --git a/build-plugins/src/main/groovy/com/cloudogu/scm/StopScmServer.groovy b/build-plugins/src/main/groovy/com/cloudogu/scm/StopScmServer.groovy new file mode 100644 index 0000000000..fd378cf6b8 --- /dev/null +++ b/build-plugins/src/main/groovy/com/cloudogu/scm/StopScmServer.groovy @@ -0,0 +1,58 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the 'Software'), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +package com.cloudogu.scm + +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.TaskAction + +class StopScmServer extends DefaultTask { + + private ScmServerExtension extension + + @Nested + ScmServerExtension getExtension() { + return extension + } + + void setExtension(ScmServerExtension extension) { + this.extension = extension + } + + @TaskAction + void exec() { + URL url = new URL("http://localhost:${extension.port}/shutdown?token=_shutdown_") + try { + HttpURLConnection connection = (HttpURLConnection )url.openConnection() + connection.setRequestMethod("POST") + // ??? + connection.getResponseCode() + } catch (IOException ex) { + // already closed ? + } + } + +} diff --git a/build-plugins/src/main/groovy/com/cloudogu/scm/WriteServerConfigTask.groovy b/build-plugins/src/main/groovy/com/cloudogu/scm/WriteServerConfigTask.groovy index f4e9f680d1..2d52969926 100644 --- a/build-plugins/src/main/groovy/com/cloudogu/scm/WriteServerConfigTask.groovy +++ b/build-plugins/src/main/groovy/com/cloudogu/scm/WriteServerConfigTask.groovy @@ -33,8 +33,9 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.Classpath -import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Nested import org.gradle.api.tasks.Internal +import org.gradle.api.GradleException import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction @@ -48,25 +49,15 @@ import groovy.json.JsonOutput class WriteServerConfigTask extends DefaultTask { - @Input - public String getHome() { - if (project.hasProperty('home')) { - return project.getProperty('home') - } - return new File(project.buildDir, 'scm-home').toString() + private ScmServerExtension extension + + @Nested + ScmServerExtension getExtension() { + return extension } - @Input - public int getPort() { - if (project.hasProperty('port')) { - return Integer.parseInt(project.getProperty('port')) - } - return 8081 - } - - @InputFile - public File getWarFile() { - return new File(project.buildDir, 'libs/scm-webapp-dev.war') + void setExtension(ScmServerExtension extension) { + this.extension = extension } @OutputFile @@ -76,17 +67,37 @@ class WriteServerConfigTask extends DefaultTask { @TaskAction void execute() { + File warFile = extension.getWarFile() + if (warFile == null) { + Configuration configuration = extension.getConfiguration() + if (configuration == null) { + throw new GradleException("warFile or configuration must be used") + } + + def artificat = configuration.resolvedConfiguration + .resolvedArtifacts + .find { + it.extension == 'war' + } + + if (artificat == null) { + throw new GradleException("could not find war file in configuration") + } + + warFile = artificat.getFile() + } + File serverConfig = getServerConfig() serverConfig.getParentFile().mkdirs() serverConfig.text = JsonOutput.toJson([ - home: getHome(), - port: getPort(), + home: extension.getHome(), + port: extension.getPort(), contextPath: '/scm', stage: 'DEVELOPMENT', headerSize: 16384, - openBrowser: true, - warFile: getWarFile().toString(), - livereloadUrl: 'http://localhost:3000' + openBrowser: extension.openBrowser, + warFile: warFile.toString(), + livereloadUrl: extension.liveReload ? 'http://localhost:3000' : null ]) } diff --git a/gradle.properties b/gradle.properties index 6911ae53a3..da9da79d53 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,4 +22,4 @@ # SOFTWARE. # -version=2.3.0-SNAPSHOT +version=2.13.0-SNAPSHOT diff --git a/scm-it/build.gradle b/scm-it/build.gradle new file mode 100644 index 0000000000..61cdeeb70d --- /dev/null +++ b/scm-it/build.gradle @@ -0,0 +1,74 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +plugins { + id 'java-library' + id 'org.scm-manager.integration-tests' +} + +configurations { + itWebApp +} + +dependencies { + testImplementation platform(project(':')) + + testImplementation project(':scm-core') + testImplementation project(':scm-test') + + // TODO add tests + testImplementation project(':scm-plugins:scm-git-plugin') + testImplementation project(path: ':scm-plugins:scm-git-plugin', configuration: 'tests') + testImplementation project(':scm-plugins:scm-hg-plugin') + testImplementation project(path: ':scm-plugins:scm-hg-plugin', configuration: 'tests') + testImplementation project(':scm-plugins:scm-svn-plugin') + testImplementation project(path: ':scm-plugins:scm-svn-plugin', configuration: 'tests') + + testImplementation 'io.rest-assured:rest-assured:4.3.0' + testImplementation 'org.glassfish:javax.json:1.1.4' + + itWebApp project(path: ':scm-webapp', configuration: 'webapp') +} + +scmServer { + configuration 'itWebApp' + openBrowser = false + liveReload = false +} + +test { + include '**/*Test.class' + exclude '**/*ITCase.class' +} + +task integrationTest(type: Test) { + include '**/*ITCase.class' + exclude '**/*Test.class' + + dependsOn 'test' + doFirst { + tasks.getByName('startScmServer').exec() + } + finalizedBy 'stopScmServer' +} diff --git a/scm-plugins/scm-git-plugin/build.gradle b/scm-plugins/scm-git-plugin/build.gradle index 4c7747e0aa..7e9e1f948f 100644 --- a/scm-plugins/scm-git-plugin/build.gradle +++ b/scm-plugins/scm-git-plugin/build.gradle @@ -28,7 +28,8 @@ plugins { } dependencies { - implementation 'sonia.jgit:org.eclipse.jgit:5.6.1.202002131546-r-scm1' + // required by scm-it + api 'sonia.jgit:org.eclipse.jgit:5.6.1.202002131546-r-scm1' implementation 'sonia.jgit:org.eclipse.jgit.http.server:5.6.1.202002131546-r-scm1' implementation 'sonia.jgit:org.eclipse.jgit.lfs.server:5.6.1.202002131546-r-scm1' @@ -51,3 +52,19 @@ scmPlugin { } } + +task testJar(type: Jar) { + classifier = 'tests' + from sourceSets.test.output +} + +configurations { + tests { + canBeConsumed = true + canBeResolved = false + } +} + +artifacts { + tests(testJar) +} diff --git a/scm-plugins/scm-hg-plugin/build.gradle b/scm-plugins/scm-hg-plugin/build.gradle index 76d3f2514c..4dbeac1dea 100644 --- a/scm-plugins/scm-hg-plugin/build.gradle +++ b/scm-plugins/scm-hg-plugin/build.gradle @@ -52,3 +52,19 @@ scmPlugin { } } + +task testJar(type: Jar) { + classifier = 'tests' + from sourceSets.test.output +} + +configurations { + tests { + canBeConsumed = true + canBeResolved = false + } +} + +artifacts { + tests(testJar) +} diff --git a/scm-plugins/scm-svn-plugin/build.gradle b/scm-plugins/scm-svn-plugin/build.gradle index 6b15062e64..dd962fd690 100644 --- a/scm-plugins/scm-svn-plugin/build.gradle +++ b/scm-plugins/scm-svn-plugin/build.gradle @@ -52,6 +52,22 @@ scmPlugin { } +task testJar(type: Jar) { + classifier = 'tests' + from sourceSets.test.output +} + +configurations { + tests { + canBeConsumed = true + canBeResolved = false + } +} + +artifacts { + tests(testJar) +} + /*