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) + } + }