mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Init command takes the output as last argument
This commit moves the --output switch to a regular argument. This aligns to other command, i.e. spring init my-project.zip would save the project to "my-project.zip" in the current directory. This commit also auto-detects the --extract option if the location ends with a slash, i.e. spring init demo/ would extract the content of the project in a demo directory that is local to the current directory. Fixes gh-1802
This commit is contained in:
parent
96198e2999
commit
2e07f003c9
@ -17,7 +17,9 @@
|
||||
package org.springframework.boot.cli.command.init;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import joptsimple.OptionSet;
|
||||
import joptsimple.OptionSpec;
|
||||
@ -27,6 +29,7 @@ import org.springframework.boot.cli.command.OptionParsingCommand;
|
||||
import org.springframework.boot.cli.command.options.OptionHandler;
|
||||
import org.springframework.boot.cli.command.status.ExitStatus;
|
||||
import org.springframework.boot.cli.util.Log;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Command} that initializes a project using Spring initializr.
|
||||
@ -45,6 +48,11 @@ public class InitCommand extends OptionParsingCommand {
|
||||
+ "Initialzr (start.spring.io)", handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageHelp() {
|
||||
return "[options] [location]";
|
||||
}
|
||||
|
||||
static class InitOptionHandler extends OptionHandler {
|
||||
|
||||
private final ServiceCapabilitiesReportGenerator serviceCapabilitiesReport;
|
||||
@ -73,8 +81,6 @@ public class InitCommand extends OptionParsingCommand {
|
||||
|
||||
private OptionSpec<Void> force;
|
||||
|
||||
private OptionSpec<String> output;
|
||||
|
||||
InitOptionHandler(InitializrService initializrService) {
|
||||
this.serviceCapabilitiesReport = new ServiceCapabilitiesReportGenerator(
|
||||
initializrService);
|
||||
@ -123,14 +129,9 @@ public class InitCommand extends OptionParsingCommand {
|
||||
|
||||
private void otherOptions() {
|
||||
this.extract = option(Arrays.asList("extract", "x"),
|
||||
"Extract the project archive");
|
||||
"Extract the project archive. Inferred if a location is specified and ends with /");
|
||||
this.force = option(Arrays.asList("force", "f"),
|
||||
"Force overwrite of existing files");
|
||||
this.output = option(
|
||||
Arrays.asList("output", "o"),
|
||||
"Location of the generated project. Can be an absolute or a "
|
||||
+ "relative reference and should refer to a directory when "
|
||||
+ "--extract is used").withRequiredArg();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -160,12 +161,17 @@ public class InitCommand extends OptionParsingCommand {
|
||||
|
||||
protected void generateProject(OptionSet options) throws IOException {
|
||||
ProjectGenerationRequest request = createProjectGenerationRequest(options);
|
||||
this.projectGenerator.generateProject(request, options.has(this.force),
|
||||
options.has(this.extract), options.valueOf(this.output));
|
||||
this.projectGenerator.generateProject(request, options.has(this.force));
|
||||
}
|
||||
|
||||
protected ProjectGenerationRequest createProjectGenerationRequest(
|
||||
OptionSet options) {
|
||||
|
||||
List<?> nonOptionArguments = new ArrayList<Object>(
|
||||
options.nonOptionArguments());
|
||||
Assert.isTrue(nonOptionArguments.size() <= 1,
|
||||
"Only the target location may be specified");
|
||||
|
||||
ProjectGenerationRequest request = new ProjectGenerationRequest();
|
||||
request.setServiceUrl(options.valueOf(this.target));
|
||||
if (options.has(this.bootVersion)) {
|
||||
@ -188,8 +194,10 @@ public class InitCommand extends OptionParsingCommand {
|
||||
if (options.has(this.type)) {
|
||||
request.setType(options.valueOf(this.type));
|
||||
}
|
||||
if (options.has(this.output)) {
|
||||
request.setOutput(options.valueOf(this.output));
|
||||
request.setExtract(options.has(this.extract));
|
||||
if (nonOptionArguments.size() == 1) {
|
||||
String output = (String) nonOptionArguments.get(0);
|
||||
request.setOutput(output);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ class ProjectGenerationRequest {
|
||||
|
||||
private String output;
|
||||
|
||||
private boolean extract;
|
||||
|
||||
private String bootVersion;
|
||||
|
||||
private List<String> dependencies = new ArrayList<String>();
|
||||
@ -76,7 +78,25 @@ class ProjectGenerationRequest {
|
||||
}
|
||||
|
||||
public void setOutput(String output) {
|
||||
this.output = output;
|
||||
if (output != null && output.endsWith("/")) {
|
||||
this.output = output.substring(0, output.length() - 1);
|
||||
this.extract = true;
|
||||
} else {
|
||||
this.output = output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the project archive should be extract in the output location. If
|
||||
* the {@link #getOutput() output} ends with "/", the project is extracted
|
||||
* automatically.
|
||||
*/
|
||||
public boolean isExtract() {
|
||||
return extract;
|
||||
}
|
||||
|
||||
public void setExtract(boolean extract) {
|
||||
this.extract = extract;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,24 +41,23 @@ public class ProjectGenerator {
|
||||
this.initializrService = initializrService;
|
||||
}
|
||||
|
||||
public void generateProject(ProjectGenerationRequest request, boolean force,
|
||||
boolean extract, String output) throws IOException {
|
||||
public void generateProject(ProjectGenerationRequest request, boolean force) throws IOException {
|
||||
ProjectGenerationResponse response = this.initializrService.generate(request);
|
||||
if (extract) {
|
||||
String fileName = (request.getOutput() != null ? request.getOutput() : response.getFileName());
|
||||
if (request.isExtract()) {
|
||||
if (isZipArchive(response)) {
|
||||
extractProject(response, output, force);
|
||||
extractProject(response, request.getOutput(), force);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
Log.info("Could not extract '" + response.getContentType() + "'");
|
||||
fileName = response.getFileName(); // Use value from the server since we can't extract it
|
||||
}
|
||||
}
|
||||
String fileName = response.getFileName();
|
||||
fileName = (fileName != null ? fileName : output);
|
||||
if (fileName == null) {
|
||||
throw new ReportableException(
|
||||
"Could not save the project, the server did not set a preferred "
|
||||
+ "file name. Use --output to specify the output location "
|
||||
+ "file name and no location was set. Specify the output location "
|
||||
+ "for the project.");
|
||||
}
|
||||
writeProject(response, fileName, force);
|
||||
@ -102,7 +101,7 @@ public class ProjectGenerator {
|
||||
throw new ReportableException(file.isDirectory() ? "Directory" : "File"
|
||||
+ " '" + file.getName()
|
||||
+ "' already exists. Use --force if you want to overwrite or "
|
||||
+ "--output to specify an alternate location.");
|
||||
+ "specify an alternate location.");
|
||||
}
|
||||
if (!entry.isDirectory()) {
|
||||
FileCopyUtils.copy(StreamUtils.nonClosing(zipStream),
|
||||
@ -123,7 +122,7 @@ public class ProjectGenerator {
|
||||
if (!overwrite) {
|
||||
throw new ReportableException("File '" + outputFile.getName()
|
||||
+ "' already exists. Use --force if you want to "
|
||||
+ "overwrite or --output to specify an alternate location.");
|
||||
+ "overwrite or specify an alternate location.");
|
||||
}
|
||||
if (!outputFile.delete()) {
|
||||
throw new ReportableException("Failed to delete existing file "
|
||||
|
@ -95,7 +95,21 @@ public class InitCommandTests extends AbstractHttpClientMockTests {
|
||||
"application/zip", "demo.zip", archive);
|
||||
mockSuccessfulProjectGeneration(request);
|
||||
assertEquals(ExitStatus.OK,
|
||||
this.command.run("--extract", "--output=" + folder.getAbsolutePath()));
|
||||
this.command.run("--extract", folder.getAbsolutePath()));
|
||||
File archiveFile = new File(folder, "test.txt");
|
||||
assertTrue("Archive not extracted properly " + folder.getAbsolutePath()
|
||||
+ " not found", archiveFile.exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateProjectAndExtractWithConvention() throws Exception {
|
||||
File folder = this.temporaryFolder.newFolder();
|
||||
byte[] archive = createFakeZipArchive("test.txt", "Fake content");
|
||||
MockHttpProjectGenerationRequest request = new MockHttpProjectGenerationRequest(
|
||||
"application/zip", "demo.zip", archive);
|
||||
mockSuccessfulProjectGeneration(request);
|
||||
assertEquals(ExitStatus.OK,
|
||||
this.command.run(folder.getAbsolutePath()+ "/"));
|
||||
File archiveFile = new File(folder, "test.txt");
|
||||
assertTrue("Archive not extracted properly " + folder.getAbsolutePath()
|
||||
+ " not found", archiveFile.exists());
|
||||
@ -113,7 +127,7 @@ public class InitCommandTests extends AbstractHttpClientMockTests {
|
||||
"application/foobar", fileName, archive);
|
||||
mockSuccessfulProjectGeneration(request);
|
||||
assertEquals(ExitStatus.OK,
|
||||
this.command.run("--extract", "--output=" + folder.getAbsolutePath()));
|
||||
this.command.run("--extract", folder.getAbsolutePath()));
|
||||
assertTrue("file should have been saved instead", file.exists());
|
||||
}
|
||||
finally {
|
||||
@ -133,7 +147,7 @@ public class InitCommandTests extends AbstractHttpClientMockTests {
|
||||
null, fileName, archive);
|
||||
mockSuccessfulProjectGeneration(request);
|
||||
assertEquals(ExitStatus.OK,
|
||||
this.command.run("--extract", "--output=" + folder.getAbsolutePath()));
|
||||
this.command.run("--extract", folder.getAbsolutePath()));
|
||||
assertTrue("file should have been saved instead", file.exists());
|
||||
}
|
||||
finally {
|
||||
@ -175,7 +189,7 @@ public class InitCommandTests extends AbstractHttpClientMockTests {
|
||||
"application/zip", "demo.zip", archive);
|
||||
mockSuccessfulProjectGeneration(request);
|
||||
assertEquals(ExitStatus.ERROR,
|
||||
this.command.run("--extract", "--output=" + folder.getAbsolutePath()));
|
||||
this.command.run("--extract", folder.getAbsolutePath()));
|
||||
assertEquals("File should not have changed", fileLength, conflict.length());
|
||||
}
|
||||
|
||||
@ -192,8 +206,7 @@ public class InitCommandTests extends AbstractHttpClientMockTests {
|
||||
mockSuccessfulProjectGeneration(request);
|
||||
assertEquals(
|
||||
ExitStatus.OK,
|
||||
this.command.run("--force", "--extract",
|
||||
"--output=" + folder.getAbsolutePath()));
|
||||
this.command.run("--force", "--extract", folder.getAbsolutePath()));
|
||||
assertTrue("File should have changed", fileLength != conflict.length());
|
||||
}
|
||||
|
||||
@ -245,12 +258,26 @@ public class InitCommandTests extends AbstractHttpClientMockTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseOutput() throws Exception {
|
||||
public void parseLocation() throws Exception {
|
||||
this.handler.disableProjectGeneration();
|
||||
this.command.run("--output=foobar.zip");
|
||||
this.command.run("foobar.zip");
|
||||
assertEquals("foobar.zip", this.handler.lastRequest.getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLocationWithSlash() throws Exception {
|
||||
this.handler.disableProjectGeneration();
|
||||
this.command.run("foobar/");
|
||||
assertEquals("foobar", this.handler.lastRequest.getOutput());
|
||||
assertTrue(this.handler.lastRequest.isExtract());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseMoreThanOneArg() throws Exception {
|
||||
this.handler.disableProjectGeneration();
|
||||
assertEquals(ExitStatus.ERROR, this.command.run("foobar", "barfoo"));
|
||||
}
|
||||
|
||||
private byte[] createFakeZipArchive(String fileName, String content)
|
||||
throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
Loading…
Reference in New Issue
Block a user