diff --git a/.locker/pom.xml b/.locker/pom.xml
index b3afb60..b4a45fe 100644
--- a/.locker/pom.xml
+++ b/.locker/pom.xml
@@ -5,7 +5,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4.0.0
io.mvnpm
- 3.0.28-SNAPSHOT
+ 3.0.29-SNAPSHOT
mvnpm-locker
pom
@@ -142,7 +142,7 @@
org.mvnpm
marked
- 12.0.0
+ 12.0.1
provided
diff --git a/pom.xml b/pom.xml
index 802d14d..50eebe7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
io.mvnpm
mvnpm
- 3.0.28-SNAPSHOT
+ 3.0.29-SNAPSHOT
mvnpm
Maven on NPM
https://mvnpm.org/
@@ -53,15 +53,27 @@
UTF-8
quarkus-bom
io.quarkus.platform
- 3.8.1
+ 3.8.2
true
3.2.5
2.23.0
1.9.0
${quarkus.platform.version}
+
+ 1.5.5
1.3.1
1.0.10
+ 24.3.7
+ 1.7.5
+ 1.0.6
+ 6.1.0
+ 12.0.1
+ 1.0.0
+ 1.0.0
+
+ 0.0.1
+ 1.3.0
@@ -147,6 +159,17 @@
io.quarkus
quarkus-jdbc-postgresql
+
+
+ org.pgpainless
+ pgpainless-core
+ ${pgpainless.version}
+
+
+ org.pgpainless
+ pgpainless-sop
+ ${pgpainless.version}
+
io.mvnpm
@@ -168,49 +191,49 @@
org.mvnpm.at.mvnpm
vaadin-webcomponents
- 24.3.7
+ ${vaadin.version}
provided
org.mvnpm.at.vaadin
router
- 1.7.5
+ ${vaadin-router.version}
provided
-
+
org.mvnpm.at.mvnpm
codeblock
- 1.0.6
+ ${codeblock.version}
provided
org.mvnpm
compare-versions
- 6.1.0
+ ${compare-versions.version}
provided
org.mvnpm
marked
- 12.0.0
+ ${marked.version}
provided
org.mvnpm.at.quarkus-webcomponents
card
- 1.0.0
+ ${card.version}
provided
org.mvnpm.at.quarkus-webcomponents
badge
- 1.0.0
+ ${badge.version}
provided
@@ -227,13 +250,13 @@
io.quarkiverse.playwright
quarkus-playwright
- 0.0.1
+ ${playwright.version}
test
io.mvnpm
esbuild-java
- 1.2.0
+ ${esbuild-java.version}
test
diff --git a/src/main/java/io/mvnpm/composite/CompositeCreator.java b/src/main/java/io/mvnpm/composite/CompositeCreator.java
index a8ec819..538d1d1 100644
--- a/src/main/java/io/mvnpm/composite/CompositeCreator.java
+++ b/src/main/java/io/mvnpm/composite/CompositeCreator.java
@@ -46,6 +46,7 @@
import io.mvnpm.file.FileStore;
import io.mvnpm.file.FileType;
import io.mvnpm.file.FileUtil;
+import io.mvnpm.file.KeyHolder;
import io.mvnpm.importmap.Aggregator;
import io.mvnpm.importmap.ImportsDataBinding;
import io.mvnpm.maven.MavenRepositoryService;
@@ -75,6 +76,9 @@ public class CompositeCreator {
@Inject
NpmRegistryFacade npmRegistryFacade;
+ @Inject
+ KeyHolder keyHolder;
+
private final MavenXpp3Reader mavenXpp3Writer = new MavenXpp3Reader();
public void buildAllComposites() {
@@ -353,7 +357,7 @@ private void mergeSource(Model pom, List dependencies) throws IOExce
}
// Also create the Sha1
FileUtil.createSha1(outputJar);
- FileUtil.createAsc(outputJar);
+ FileUtil.createAsc(keyHolder.getSecretKeyRing(), outputJar);
FileUtil.createMd5(outputJar);
}
}
diff --git a/src/main/java/io/mvnpm/file/FileUtil.java b/src/main/java/io/mvnpm/file/FileUtil.java
index 198962e..c186232 100644
--- a/src/main/java/io/mvnpm/file/FileUtil.java
+++ b/src/main/java/io/mvnpm/file/FileUtil.java
@@ -17,8 +17,15 @@
import jakarta.ws.rs.core.StreamingOutput;
+import org.bouncycastle.openpgp.PGPSecretKeyRing;
+import org.pgpainless.sop.SOPImpl;
+
import io.mvnpm.Constants;
import io.quarkus.logging.Log;
+import sop.ByteArrayAndResult;
+import sop.ReadyWithResult;
+import sop.SOP;
+import sop.SigningResult;
public class FileUtil {
@@ -151,34 +158,34 @@ private static String getMd5(java.io.InputStream inputStream) {
}
}
- public static boolean createAsc(Path localFilePath) {
- return FileUtil.createAsc(localFilePath, false);
- }
+ public static boolean createAsc(PGPSecretKeyRing secretKeyRing, Path localFilePath) {
+ if (secretKeyRing != null) {
+ String outputFile = localFilePath.toString() + Constants.DOT_ASC;
+ Path ascFileOutput = Paths.get(outputFile);
+ synchronized (ascFileOutput) {
+ if (!Files.exists(ascFileOutput)) {
+
+ try {
+ byte[] jarFileBytes = Files.readAllBytes(localFilePath);
+
+ SOP sop = new SOPImpl();
+ ReadyWithResult readyWithResult = sop.detachedSign()
+ .key(secretKeyRing.getSecretKey().getEncoded())
+ .data(jarFileBytes);
- public static boolean createAsc(Path localFilePath, boolean force) {
- String outputFile = localFilePath.toString() + Constants.DOT_ASC;
- Path f = Paths.get(outputFile);
- synchronized (f) {
- if (!Files.exists(f) || force) {
- try {
- Process process = Runtime.getRuntime().exec(GPG_COMMAND + localFilePath.toString());
- // Set a timeout of 10 seconds
- long timeout = 20;
- boolean processFinished = process.waitFor(timeout, TimeUnit.SECONDS);
-
- if (!processFinished) {
- process.destroy(); // If the process doesn't finish, we can destroy it
+ ByteArrayAndResult bytesAndResult = readyWithResult.toByteArrayAndResult();
+
+ byte[] detachedSignature = bytesAndResult.getBytes();
+
+ Files.write(ascFileOutput, detachedSignature);
+ } catch (IOException e) {
+ e.printStackTrace();
return false;
- } else {
- // Process finished within the timeout
- int exitCode = process.exitValue();
- process.destroy();
- return true;
}
- } catch (InterruptedException | IOException ex) {
- throw new IllegalStateException(ex);
}
}
+ } else {
+ return false;
}
return true;
}
@@ -194,6 +201,4 @@ private static byte[] getMessageDigest(InputStream inputStream, String algorithm
return md.digest();
}
-
- private static final String GPG_COMMAND = "gpg -ab ";
}
diff --git a/src/main/java/io/mvnpm/file/KeyHolder.java b/src/main/java/io/mvnpm/file/KeyHolder.java
new file mode 100644
index 0000000..347505c
--- /dev/null
+++ b/src/main/java/io/mvnpm/file/KeyHolder.java
@@ -0,0 +1,51 @@
+package io.mvnpm.file;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Optional;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+
+import org.bouncycastle.openpgp.PGPSecretKeyRing;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.pgpainless.PGPainless;
+
+import io.quarkus.runtime.StartupEvent;
+
+/**
+ * Holds the key for signing the files
+ *
+ * @author Phillip Kruger (phillip.kruger@gmail.com)
+ */
+@ApplicationScoped
+public class KeyHolder {
+
+ private PGPSecretKeyRing secretKeyRing = null;
+
+ @ConfigProperty(name = "mvnpm.asckey.path")
+ Optional asckeyPath;
+
+ void onStart(@Observes StartupEvent ev) {
+ if (asckeyPath.isPresent()) {
+ try {
+ Path keyFilePath = Paths.get(asckeyPath.get());
+ byte[] keyBytes = Files.readAllBytes(keyFilePath);
+ this.secretKeyRing = PGPainless.readKeyRing().secretKeyRing(keyBytes);
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ public PGPSecretKeyRing getSecretKeyRing() {
+ return this.secretKeyRing;
+ }
+
+ public boolean hasSecretKeyRing() {
+ return this.secretKeyRing != null;
+ }
+
+}
diff --git a/src/main/java/io/mvnpm/file/type/JarClient.java b/src/main/java/io/mvnpm/file/type/JarClient.java
index cd5bd6d..b6a44cf 100644
--- a/src/main/java/io/mvnpm/file/type/JarClient.java
+++ b/src/main/java/io/mvnpm/file/type/JarClient.java
@@ -27,7 +27,7 @@
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
-import org.apache.commons.compress.utils.IOUtils;
+import org.apache.commons.io.IOUtils;
import io.mvnpm.Constants;
import io.mvnpm.file.FileStore;
@@ -114,10 +114,10 @@ private void tgzToJar(io.mvnpm.npm.model.Package p, Path tgzPath, JarArchiveOutp
try (InputStream tgzInputStream = Files.newInputStream(tgzPath);
GzipCompressorInputStream gzipInputStream = new GzipCompressorInputStream(tgzInputStream);
- TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(gzipInputStream);) {
+ TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(gzipInputStream)) {
final Map toTgz = new LinkedHashMap<>();
- for (TarArchiveEntry entry = tarArchiveInputStream.getNextTarEntry(); entry != null; entry = tarArchiveInputStream
- .getNextTarEntry()) {
+ for (TarArchiveEntry entry = tarArchiveInputStream.getNextEntry(); entry != null; entry = tarArchiveInputStream
+ .getNextEntry()) {
tgzEntryToJarEntry(p, entry, tarArchiveInputStream, toTgz, jarOutput);
}
if (!toTgz.isEmpty()) {
@@ -135,33 +135,37 @@ private void tgzEntryToJarEntry(io.mvnpm.npm.model.Package p, ArchiveEntry entry
String name = entry.getName();
final boolean shouldAdd = !matches(FILES_TO_EXCLUDE, name);
final boolean shouldTgz = matches(FILES_TO_TGZ, name);
- if (shouldAdd || shouldTgz) {
- name = name.replaceFirst(NPM_ROOT, Constants.EMPTY);
- // do not add entries that will result in invalid zip file systems that will not be able to be opened
- // by quarkus because it uses the ZipFileSystem implementation.
- final String jarEntryPath = MVN_ROOT + importMapRoot + name;
- final String tarEntryPath = importMapRoot + name;
- // paths that include "/./" or "/../" as path element are invalid
- if (jarEntryPath.startsWith("./") || jarEntryPath.contains("/./")
- || (shouldTgz && (tarEntryPath.startsWith(".") || tarEntryPath.contains("/./")))) {
- return;
- }
- try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
- BufferedOutputStream bos = new BufferedOutputStream(baos, bufferSize)) {
- IOUtils.copy(tar, bos, bufferSize);
- bos.flush();
- baos.flush();
- if (shouldAdd) {
- writeJarEntry(jarOutput, jarEntryPath, baos.toByteArray());
- } else {
- // We don't add the META-INF because the tgz is already in META-INF
- toTgz.put("resources" + importMapRoot + name, baos.toByteArray());
- }
+ name = name.replaceFirst(NPM_ROOT, Constants.EMPTY);
+ // do not add entries that will result in invalid zip file systems that will not be able to be opened
+ // by quarkus because it uses the ZipFileSystem implementation.
+ final String jarEntryPath = MVN_ROOT + importMapRoot + name;
+ final String tarEntryPath = importMapRoot + name;
+ final boolean isRelativeLink = isRelativeLink(jarEntryPath, tarEntryPath, shouldTgz);
+
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ BufferedOutputStream bos = new BufferedOutputStream(baos, bufferSize)) {
+ IOUtils.copy(tar, bos, bufferSize);
+ bos.flush();
+ baos.flush();
+ if (shouldAdd && !isRelativeLink) {
+ writeJarEntry(jarOutput, jarEntryPath, baos.toByteArray());
+ } else if (shouldTgz && !isRelativeLink) {
+ // We don't add the META-INF because the tgz is already in META-INF
+ toTgz.put("resources" + importMapRoot + name, baos.toByteArray());
}
}
}
+ private boolean isRelativeLink(final String jarEntryPath, final String tarEntryPath, final boolean shouldTgz) {
+ // paths that include "/./" or "/../" as path element are invalid
+ if (jarEntryPath.startsWith("./") || jarEntryPath.contains("/./")
+ || (shouldTgz && (tarEntryPath.startsWith(".") || tarEntryPath.contains("/./")))) {
+ return true;
+ }
+ return false;
+ }
+
private byte[] tarGz(Map toCompress) throws IOException {
// Step 1, 2 and 3: Create tar archive from map
ByteArrayOutputStream tarOutput = new ByteArrayOutputStream();
diff --git a/src/main/java/io/mvnpm/newfile/AscService.java b/src/main/java/io/mvnpm/newfile/AscService.java
index 1556c8b..88e1ac8 100644
--- a/src/main/java/io/mvnpm/newfile/AscService.java
+++ b/src/main/java/io/mvnpm/newfile/AscService.java
@@ -1,9 +1,11 @@
package io.mvnpm.newfile;
import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
import io.mvnpm.file.FileStoreEvent;
import io.mvnpm.file.FileUtil;
+import io.mvnpm.file.KeyHolder;
import io.quarkus.logging.Log;
import io.quarkus.vertx.ConsumeEvent;
import io.smallrye.common.annotation.Blocking;
@@ -16,10 +18,13 @@
@ApplicationScoped
public class AscService {
+ @Inject
+ KeyHolder keyHolder;
+
@ConsumeEvent("new-file-created")
@Blocking
public void newFileCreated(FileStoreEvent fse) {
- boolean success = FileUtil.createAsc(fse.filePath());
+ boolean success = FileUtil.createAsc(keyHolder.getSecretKeyRing(), fse.filePath());
if (!success) {
Log.warn("file signed " + fse.filePath() + ".asc [failed] trying again");
}
diff --git a/src/test/java/io/mvnpm/MavenRepositoryApiTest.java b/src/test/java/io/mvnpm/MavenRepositoryApiTest.java
index cf69a22..0e351a5 100644
--- a/src/test/java/io/mvnpm/MavenRepositoryApiTest.java
+++ b/src/test/java/io/mvnpm/MavenRepositoryApiTest.java
@@ -28,7 +28,7 @@ public class MavenRepositoryApiTest {
@Test
public void testBasicPom() {
RestAssured.given().header("User-Agent", "m2e/unit-test")
- .when().get("/maven2/org/mvnpm/lit/2.4.0/lit-2.4.0.pom")
+ .when().get("/maven2/org/mvnpm/lit/3.1.2/lit-3.1.2.pom")
.then().log().all().and()
.statusCode(200);
}
@@ -36,7 +36,7 @@ public void testBasicPom() {
@Test
public void testStarDependencyPom() {
RestAssured.given().header("User-Agent", "m2e/unit-test")
- .when().get("/maven2/org/mvnpm/at/types/codemirror/5.60.5/codemirror-5.60.5.pom")
+ .when().get("/maven2/org/mvnpm/at/types/codemirror/5.60.15/codemirror-5.60.15.pom")
.then().log().all().and()
.statusCode(200);
}
@@ -44,7 +44,7 @@ public void testStarDependencyPom() {
@Test
public void testIgnoreBetaPom() {
RestAssured.given().header("User-Agent", "m2e/unit-test")
- .when().get("/maven2/org/mvnpm/at/vaadin/tabs/23.2.3/vaadin-23.2.3.pom")
+ .when().get("/maven2/org/mvnpm/at/vaadin/tabs/24.3.8/vaadin-24.3.8.pom")
.then().log().all().and()
.statusCode(200);
}
@@ -77,11 +77,11 @@ public void testComposite() throws IOException {
.setParam(CoreConnectionPNames.SO_TIMEOUT, 300000));
final byte[] jar = RestAssured.given().header("User-Agent", "m2e/unit-test")
.config(config)
- .when().get("/maven2/org/mvnpm/at/mvnpm/vaadin-webcomponents/24.2.5/vaadin-webcomponents-24.2.5.jar")
+ .when().get("/maven2/org/mvnpm/at/mvnpm/vaadin-webcomponents/24.3.8/vaadin-webcomponents-24.3.8.jar")
.then()
.statusCode(200)
.extract().asByteArray();
- final Path tempFile = Files.createTempFile("vaadin-webcomponents-24.2.5", ".jar");
+ final Path tempFile = Files.createTempFile("vaadin-webcomponents-24.3.8", ".jar");
final Path nodeModules = Files.createTempDirectory("node_modules");
Files.write(tempFile, jar);
WebDepsInstaller.install(nodeModules,