diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md
index 7be07385af..bd179eed97 100644
--- a/docs/pages/release_notes.md
+++ b/docs/pages/release_notes.md
@@ -357,6 +357,7 @@ The rules have been moved into categories with PMD 6.
* [#4823](https://github.com/pmd/pmd/pull/4823): Update to use renamed pmd-designer
* [#4827](https://github.com/pmd/pmd/pull/4827): \[compat6] Support config errors and cpd for csharp
* [#4830](https://github.com/pmd/pmd/issues/4830): Consolidate packages in each maven module
+ * [#4867](https://github.com/pmd/pmd/issues/4867): \[dist] ./mvnw command not found in dist-src
* apex
* [#3766](https://github.com/pmd/pmd/issues/3766): \[apex] Replace Jorje with fully open source front-end
* [#4828](https://github.com/pmd/pmd/issues/4828): \[apex] Support null coalescing operator ?? (apex 60)
diff --git a/docs/pages/release_notes_pmd7.md b/docs/pages/release_notes_pmd7.md
index 70db35e81f..68f0009144 100644
--- a/docs/pages/release_notes_pmd7.md
+++ b/docs/pages/release_notes_pmd7.md
@@ -3164,6 +3164,7 @@ No changes.
* [#4823](https://github.com/pmd/pmd/pull/4823): Update to use renamed pmd-designer
* [#4827](https://github.com/pmd/pmd/pull/4827): \[compat6] Support config errors and cpd for csharp
* [#4830](https://github.com/pmd/pmd/issues/4830): Consolidate packages in each maven module
+ * [#4867](https://github.com/pmd/pmd/issues/4867): \[dist] ./mvnw command not found in dist-src
* ant
* [#4080](https://github.com/pmd/pmd/issues/4080): \[ant] Split off Ant integration into a new submodule
* core
diff --git a/pmd-dist/src/main/resources/assemblies/pmd-src.xml b/pmd-dist/src/main/resources/assemblies/pmd-src.xml
index 6f13016a9a..3b49591b74 100644
--- a/pmd-dist/src/main/resources/assemblies/pmd-src.xml
+++ b/pmd-dist/src/main/resources/assemblies/pmd-src.xml
@@ -10,11 +10,22 @@
pmd-src-${project.version}
+
+ false
+ ${project.basedir}/..
+
+
+ mvnw
+
+ 0755
+
+
false
${project.basedir}/..
+ mvnw
.git/**
**/target/**
**/bin/**
@@ -40,6 +51,7 @@
docs/.bundle/**
docs/vendor/**
docs/_site/**
+ node_modules/**
pmd-core/dependency-reduced-pom.xml
diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/dist/SourceDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/dist/SourceDistributionIT.java
index 6ae1f4ba52..707dd70cc8 100644
--- a/pmd-dist/src/test/java/net/sourceforge/pmd/dist/SourceDistributionIT.java
+++ b/pmd-dist/src/test/java/net/sourceforge/pmd/dist/SourceDistributionIT.java
@@ -4,6 +4,7 @@
package net.sourceforge.pmd.dist;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -12,6 +13,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipFile;
import org.junit.jupiter.api.Test;
import net.sourceforge.pmd.PMDVersion;
@@ -28,6 +31,14 @@ class SourceDistributionIT {
assertTrue(getSourceDistribution().exists());
}
+ @Test
+ void mavenWrapperShouldBeExecutable() throws Exception {
+ try (ZipFile zip = ZipFile.builder().setFile(getSourceDistribution()).get()) {
+ ZipArchiveEntry mavenWrapper = zip.getEntry(BASE_PATH + "/mvnw");
+ assertEquals(ZipFileExtractor.OWNER_EXECUTABLE, mavenWrapper.getUnixMode() & ZipFileExtractor.OWNER_EXECUTABLE);
+ }
+ }
+
@Test
void verifyExclusions() throws Exception {
Set exclusions = new HashSet<>();
@@ -35,6 +46,7 @@ class SourceDistributionIT {
exclusions.add(BASE_PATH + "/.ci/files/private-env");
exclusions.add(BASE_PATH + "/.ci/files/public-env");
exclusions.add(BASE_PATH + "/.ci/files/release-signing-key-D0BF1D737C9A1C22.gpg.gpg");
+ exclusions.add(BASE_PATH + "/node_modules/.bin/all-contributors");
List files = ZipFileExtractor.readZipFile(getSourceDistribution().toPath());
for (String file : files) {
diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/dist/ZipFileExtractor.java b/pmd-dist/src/test/java/net/sourceforge/pmd/dist/ZipFileExtractor.java
index 301e8078b4..07b22f094e 100644
--- a/pmd-dist/src/test/java/net/sourceforge/pmd/dist/ZipFileExtractor.java
+++ b/pmd-dist/src/test/java/net/sourceforge/pmd/dist/ZipFileExtractor.java
@@ -7,9 +7,9 @@ package net.sourceforge.pmd.dist;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
@@ -27,7 +27,7 @@ import net.sourceforge.pmd.internal.util.IOUtil;
*/
public class ZipFileExtractor {
// unix file permission for executable flag by owner
- private static final int OWNER_EXECUTABLE = 0x40;
+ static final int OWNER_EXECUTABLE = 0x40;
private ZipFileExtractor() {
// Helper class
@@ -40,7 +40,7 @@ public class ZipFileExtractor {
* @throws Exception if any error happens during extraction
*/
public static void extractZipFile(Path zipPath, Path tempDir) throws Exception {
- try (ZipFile zip = new ZipFile(zipPath.toFile())) {
+ try (ZipFile zip = ZipFile.builder().setFile(zipPath.toFile()).get()) {
Enumeration entries = zip.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
@@ -49,11 +49,11 @@ public class ZipFileExtractor {
assertTrue(file.mkdirs());
} else {
try (InputStream data = zip.getInputStream(entry);
- OutputStream fileOut = new FileOutputStream(file);) {
+ OutputStream fileOut = Files.newOutputStream(file.toPath());) {
IOUtil.copy(data, fileOut);
}
if ((entry.getUnixMode() & OWNER_EXECUTABLE) == OWNER_EXECUTABLE) {
- file.setExecutable(true);
+ assertTrue(file.setExecutable(true));
}
}
}
@@ -68,7 +68,7 @@ public class ZipFileExtractor {
*/
public static List readZipFile(Path zipPath) throws Exception {
List result = new ArrayList<>();
- try (ZipFile zip = new ZipFile(zipPath.toFile())) {
+ try (ZipFile zip = ZipFile.builder().setFile(zipPath.toFile()).get()) {
Enumeration entries = zip.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();