From 3b0509a9bbe3865c011184e9a5650a259a5e8153 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 15 Jan 2015 11:06:22 +0100 Subject: [PATCH] Add baseDir option Previously the generated archive wasn't self contained and the user had to create a dedicated directory before extracting it in case the archive utility does not handle that use case (MacOS supports that by default). A baseDir request attribute has been added and generate a base directory in the archive when set. It is enabled by default in the web UI and takes the same value as the artifactId. Other clients are unaffected by this change (and shouldn't probably). Note that the legacy HTML form isn't impacted by this change either as older STS versions expect the archive to have a very specific format. curl-based tools can use that new baseDir request parameter to specify that a base directory is required, something like curl https://start.spring.io/starter.tgz -d baseDir=my-project Closes gh-65 --- CHANGELOG.adoc | 3 +++ .../spring/initializr/ProjectGenerator.groovy | 22 ++++++++++++++----- .../spring/initializr/ProjectRequest.groovy | 3 +++ .../src/main/resources/templates/home.html | 12 +++++++--- .../initializr/ProjectGeneratorTests.groovy | 9 ++++++++ .../initializr/test/ProjectAssert.groovy | 16 ++++++++++++++ ...lizerControllerFormIntegrationTests.groovy | 17 ++++++++------ .../MainControllerFormIntegrationTests.groovy | 12 +++++++++- 8 files changed, 78 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index ea5125b3..3105681e 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -7,6 +7,9 @@ order. === Release 1.0.0 (In progress) +* https://github.com/spring-io/initializr/issues/65[#64]: project archives generated by the web UI now contain +a base directory equals to the `artifactId`. By default, no base directory gets created if the project is +generated by another means; use the new `baseDir` to control that behaviour. * https://github.com/spring-io/initializr/issues/46[#46]: projects generated against Spring 1.2+ are using the new `@SpringBootApplication` * https://github.com/spring-io/initializr/issues/64[#64]: Groovy-based projects are now working properly with diff --git a/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy b/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy index ee6d3771..89dcd3f3 100644 --- a/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy @@ -74,10 +74,12 @@ class ProjectGenerator { File generateProjectStructure(ProjectRequest request) { def model = initializeModel(request) - def dir = File.createTempFile('tmp', '', new File(tmpdir)) - addTempFile(dir.name, dir) - dir.delete() - dir.mkdirs() + def rootDir = File.createTempFile('tmp', '', new File(tmpdir)) + addTempFile(rootDir.name, rootDir) + rootDir.delete() + rootDir.mkdirs() + + def dir = initializerProjectDir(rootDir, request) if ('gradle'.equals(request.build)) { def gradle = new String(doGenerateGradleBuild(model)) @@ -119,7 +121,7 @@ class ProjectGenerator { new File(dir, 'src/main/resources/static').mkdirs() } invokeListeners(request) - dir + rootDir } @@ -183,6 +185,16 @@ class ProjectGenerator { template 'starter-build.gradle', model } + private File initializerProjectDir(File rootDir, ProjectRequest request) { + if (request.baseDir) { + File dir = new File(rootDir, request.baseDir) + dir.mkdir() + return dir + } else { + return rootDir + } + } + def write(File target, String templateName, def model) { def tmpl = templateName.endsWith('.groovy') ? templateName + '.tmpl' : templateName def body = template tmpl, model diff --git a/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy b/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy index e9fc3465..19b80bda 100644 --- a/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/ProjectRequest.groovy @@ -53,6 +53,9 @@ class ProjectRequest { String packageName String javaVersion + // The base directory to create in the archive - no baseDir by default + String baseDir + // Resolved dependencies based on the ids provided by either "style" or "dependencies" List resolvedDependencies diff --git a/initializr/src/main/resources/templates/home.html b/initializr/src/main/resources/templates/home.html index 8a991148..45af8a98 100644 --- a/initializr/src/main/resources/templates/home.html +++ b/initializr/src/main/resources/templates/home.html @@ -26,6 +26,7 @@
+

Project metadata

@@ -38,7 +39,7 @@
- +
@@ -62,7 +63,7 @@
- <% types.each { %> <% } %> @@ -162,9 +163,14 @@
diff --git a/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy b/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy index d141c406..d9b5cb18 100644 --- a/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy @@ -205,6 +205,15 @@ class ProjectGeneratorTests { .doesNotContain('@EnableAutoConfiguration', '@Configuration', '@ComponentScan') } + @Test + void customBaseDirectory() { + def request = createProjectRequest() + request.baseDir = 'my-project' + generateProject(request).hasBaseDir('my-project') + .isJavaProject() + .isMavenProject() + } + PomAssert generateMavenPom(ProjectRequest request) { def content = new String(projectGenerator.generateMavenPom(request)) new PomAssert(content).validateProjectRequest(request) diff --git a/initializr/src/test/groovy/io/spring/initializr/test/ProjectAssert.groovy b/initializr/src/test/groovy/io/spring/initializr/test/ProjectAssert.groovy index 47ad9de1..0774a9f6 100644 --- a/initializr/src/test/groovy/io/spring/initializr/test/ProjectAssert.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/test/ProjectAssert.groovy @@ -19,6 +19,7 @@ package io.spring.initializr.test import io.spring.initializr.InitializrMetadata import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertTrue /** * Various project based assertions. @@ -40,6 +41,21 @@ class ProjectAssert { this.dir = dir } + /** + * Validate that the project contains a base directory with the specified name. + *

When extracting such archive, a directory with the specified {@code name} + * will be created with the content of the project instead of extracting it in + * the directory itself. + * @param name the expected name of the base directory + * @return an updated project assert on that base directory + */ + ProjectAssert hasBaseDir(String name) { + File projectDir = file(name) + assertTrue "No directory $name found in $dir.absolutePath", projectDir.exists() + assertTrue "$name is not a directory", projectDir.isDirectory() + new ProjectAssert(projectDir) // Replacing the root dir so that other assertions match the root + } + /** * Return a {@link PomAssert} for this project. */ diff --git a/initializr/src/test/groovy/io/spring/initializr/web/AbstractInitializerControllerFormIntegrationTests.groovy b/initializr/src/test/groovy/io/spring/initializr/web/AbstractInitializerControllerFormIntegrationTests.groovy index 8ab10e8a..4b4c76aa 100644 --- a/initializr/src/test/groovy/io/spring/initializr/web/AbstractInitializerControllerFormIntegrationTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/web/AbstractInitializerControllerFormIntegrationTests.groovy @@ -67,7 +67,7 @@ abstract class AbstractInitializerControllerFormIntegrationTests extends Abstrac @Test void createDefaultJavaProject() { def page = home() - def projectAssert = zipProjectAssert(page.generateProject().contentAsStream.bytes) + def projectAssert = zipProjectAssert(page, page.generateProject()) projectAssert.isMavenProject().isJavaProject().hasStaticAndTemplatesResources(false) .pomAssert().hasDependenciesCount(2) .hasSpringBootStarterRootDependency().hasSpringBootStarterDependency('test') @@ -77,7 +77,7 @@ abstract class AbstractInitializerControllerFormIntegrationTests extends Abstrac void createDefaultGroovyProject() { def page = home() page.language = 'groovy' - def projectAssert = zipProjectAssert(page.generateProject().contentAsStream.bytes) + def projectAssert = zipProjectAssert(page, page.generateProject()) projectAssert.isMavenProject().isGroovyProject().hasStaticAndTemplatesResources(false) .pomAssert().hasDependenciesCount(3) .hasSpringBootStarterRootDependency().hasSpringBootStarterDependency('test') @@ -92,7 +92,7 @@ abstract class AbstractInitializerControllerFormIntegrationTests extends Abstrac String value = webResponse.getResponseHeaderValue('Content-Disposition') assertEquals 'attachment; filename="foo-bar.zip"', value - def projectAssert = zipProjectAssert(webResponse) + def projectAssert = zipProjectAssert(page, webResponse) projectAssert.isMavenProject().isJavaProject('MyProjectApplication') .hasStaticAndTemplatesResources(true) @@ -108,7 +108,7 @@ abstract class AbstractInitializerControllerFormIntegrationTests extends Abstrac String value = webResponse.getResponseHeaderValue('Content-Disposition') assertEquals 'attachment; filename="foo-bar.zip"', value - def projectAssert = zipProjectAssert(webResponse) + def projectAssert = zipProjectAssert(page, webResponse) projectAssert.isMavenProject().isGroovyProject('MyProjectApplication') .hasStaticAndTemplatesResources(true) @@ -141,7 +141,7 @@ abstract class AbstractInitializerControllerFormIntegrationTests extends Abstrac void createWarProject() { def page = home() page.packaging = 'war' - def projectAssert = zipProjectAssert(page.generateProject()) + def projectAssert = zipProjectAssert(page, page.generateProject()) projectAssert.isMavenProject().isJavaWarProject() .pomAssert().hasPackaging('war').hasDependenciesCount(3) .hasSpringBootStarterDependency('web') // Added with war packaging @@ -153,7 +153,7 @@ abstract class AbstractInitializerControllerFormIntegrationTests extends Abstrac def page = home() page.type = projectType page.dependencies << 'data-jpa' - def projectAssert = zipProjectAssert(page.generateProject()) + def projectAssert = zipProjectAssert(page, page.generateProject()) projectAssert.isGradleProject().isJavaProject().hasStaticAndTemplatesResources(false) } @@ -163,7 +163,10 @@ abstract class AbstractInitializerControllerFormIntegrationTests extends Abstrac createHomePage(home) } - ProjectAssert zipProjectAssert(WebResponse webResponse) { + /** + * Initialize a {@link ProjectAssert} for the specified {@link HomePage} and {@link WebResponse}. + */ + protected ProjectAssert zipProjectAssert(HomePage page, WebResponse webResponse) { zipProjectAssert(webResponse.contentAsStream.bytes) } diff --git a/initializr/src/test/groovy/io/spring/initializr/web/MainControllerFormIntegrationTests.groovy b/initializr/src/test/groovy/io/spring/initializr/web/MainControllerFormIntegrationTests.groovy index a41c8759..fc3d927f 100644 --- a/initializr/src/test/groovy/io/spring/initializr/web/MainControllerFormIntegrationTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/web/MainControllerFormIntegrationTests.groovy @@ -16,7 +16,10 @@ package io.spring.initializr.web +import com.gargoylesoftware.htmlunit.WebResponse import com.gargoylesoftware.htmlunit.html.HtmlPage +import io.spring.initializr.InitializrMetadata +import io.spring.initializr.test.ProjectAssert import io.spring.initializr.web.support.DefaultHomePage import io.spring.initializr.web.support.HomePage @@ -27,7 +30,6 @@ import io.spring.initializr.web.support.HomePage */ class MainControllerFormIntegrationTests extends AbstractInitializerControllerFormIntegrationTests { - @Override void createSimpleGradleProject() { createSimpleGradleProject('gradle-project') @@ -43,4 +45,12 @@ class MainControllerFormIntegrationTests extends AbstractInitializerControllerFo new DefaultHomePage(home) } + @Override + protected ProjectAssert zipProjectAssert(HomePage page, WebResponse webResponse) { + ProjectAssert projectAssert = super.zipProjectAssert(page, webResponse) + // we require self contained archive by default + String dirName = page.artifactId ?: InitializrMetadata.Defaults.DEFAULT_NAME + projectAssert.hasBaseDir(dirName) + } + }