From 662fef2c830b03f76ab062e958cebc68ae5f84e8 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 16 Apr 2026 10:51:35 +0100 Subject: [PATCH] Normalize filename for single file downloads Closes gh-50087 --- .../cli/command/init/InitializrService.java | 3 +- .../cli/command/init/ProjectGenerator.java | 2 +- .../cli/command/init/InitCommandTests.java | 48 ++++++++++++------- .../command/init/InitializrServiceTests.java | 4 +- 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/InitializrService.java b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/InitializrService.java index 54e72514530..1a8b891b8b9 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/InitializrService.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/InitializrService.java @@ -16,6 +16,7 @@ package org.springframework.boot.cli.command.init; +import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.charset.Charset; @@ -242,7 +243,7 @@ class InitializrService { value = value.substring(start + FILENAME_HEADER_PREFIX.length()); int end = value.indexOf('\"'); if (end != -1) { - return value.substring(0, end); + return new File(value.substring(0, end)).getName(); } } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/ProjectGenerator.java b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/ProjectGenerator.java index 07dd3a37823..9d79bb33683 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/ProjectGenerator.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/init/ProjectGenerator.java @@ -131,7 +131,7 @@ class ProjectGenerator { } private void writeProject(ProjectGenerationResponse entity, String output, boolean overwrite) throws IOException { - File outputFile = new File(output); + File outputFile = new File(System.getProperty("user.dir"), output); if (outputFile.exists()) { if (!overwrite) { throw new ReportableException( diff --git a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java index 635698fcbcf..52b3098eab6 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java @@ -26,6 +26,7 @@ import java.util.zip.ZipOutputStream; import joptsimple.OptionSet; import org.apache.hc.core5.http.HttpHost; +import org.assertj.core.api.SoftAssertionsProvider.ThrowingRunnable; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; @@ -213,26 +214,30 @@ class InitCommandTests extends AbstractHttpClientMockTests { @Test void fileNotOverwrittenByDefault(@TempDir File tempDir) throws Exception { - File file = new File(tempDir, "test.file"); - file.createNewFile(); - long fileLength = file.length(); - MockHttpProjectGenerationRequest request = new MockHttpProjectGenerationRequest("application/zip", - file.getAbsolutePath()); - mockSuccessfulProjectGeneration(request); - assertThat(this.command.run()).as("Should have failed").isEqualTo(ExitStatus.ERROR); - assertThat(file.length()).as("File should not have changed").isEqualTo(fileLength); + withUserDir(tempDir, () -> { + File file = new File(tempDir, "test.file"); + file.createNewFile(); + long fileLength = file.length(); + MockHttpProjectGenerationRequest request = new MockHttpProjectGenerationRequest("application/zip", + file.getAbsolutePath()); + mockSuccessfulProjectGeneration(request); + assertThat(this.command.run()).as("Should have failed").isEqualTo(ExitStatus.ERROR); + assertThat(file.length()).as("File should not have changed").isEqualTo(fileLength); + }); } @Test void overwriteFile(@TempDir File tempDir) throws Exception { - File file = new File(tempDir, "test.file"); - file.createNewFile(); - long fileLength = file.length(); - MockHttpProjectGenerationRequest request = new MockHttpProjectGenerationRequest("application/zip", - file.getAbsolutePath()); - mockSuccessfulProjectGeneration(request); - assertThat(this.command.run("--force")).isEqualTo(ExitStatus.OK); - assertThat(fileLength).as("File should have changed").isNotEqualTo(file.length()); + withUserDir(tempDir, () -> { + File file = new File(tempDir, "test.file"); + file.createNewFile(); + long fileLength = file.length(); + MockHttpProjectGenerationRequest request = new MockHttpProjectGenerationRequest("application/zip", + file.getAbsolutePath()); + mockSuccessfulProjectGeneration(request); + assertThat(this.command.run("--force")).isEqualTo(ExitStatus.OK); + assertThat(fileLength).as("File should have changed").isNotEqualTo(file.length()); + }); } @Test @@ -399,6 +404,17 @@ class InitCommandTests extends AbstractHttpClientMockTests { request.getHeaders("User-Agent")[0].getValue().startsWith("SpringBootCli/"))), isNull()); } + private void withUserDir(File userDir, ThrowingRunnable action) throws Exception { + String previous = System.getProperty("user.dir"); + System.setProperty("user.dir", userDir.getAbsolutePath()); + try { + action.run(); + } + finally { + System.setProperty("user.dir", previous); + } + } + private byte[] createFakeZipArchive(String fileName, String content) throws IOException { try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { try (ZipOutputStream zos = new ZipOutputStream(bos)) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceTests.java b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceTests.java index 300bdf25698..9696bba974e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceTests.java @@ -49,9 +49,9 @@ class InitializrServiceTests extends AbstractHttpClientMockTests { void generateSimpleProject() throws Exception { ProjectGenerationRequest request = new ProjectGenerationRequest(); MockHttpProjectGenerationRequest mockHttpRequest = new MockHttpProjectGenerationRequest("application/xml", - "foo.zip"); + "nested/path/foo.zip"); ProjectGenerationResponse entity = generateProject(request, mockHttpRequest); - assertProjectEntity(entity, mockHttpRequest.contentType, mockHttpRequest.fileName); + assertProjectEntity(entity, mockHttpRequest.contentType, "foo.zip"); } @Test