From 08f8dc9af2ed275d7b76fd3d3229e73e39dd4e93 Mon Sep 17 00:00:00 2001 From: prathamalwayscomeslast Date: Wed, 25 Mar 2026 03:05:14 +0530 Subject: [PATCH 1/7] feat: Checksum algorithm now configurable Signed-off-by: PRATHAM ~ --- .../hudson/plugins/s3/S3BucketPublisher.java | 10 ++++++- .../java/hudson/plugins/s3/S3Profile.java | 27 ++++++++++++++----- .../s3/callable/S3BaseUploadCallable.java | 12 ++++++++- .../plugins/s3/callable/S3GzipCallable.java | 7 ++++- .../plugins/s3/callable/S3UploadCallable.java | 7 ++++- 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java index 2195d8be..9507397b 100644 --- a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java +++ b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java @@ -46,6 +46,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST; import software.amazon.awssdk.core.exception.SdkException; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; import java.io.IOException; import java.io.PrintStream; @@ -69,6 +70,7 @@ public final class S3BucketPublisher extends Recorder implements SimpleBuildStep private boolean dontWaitForConcurrentBuildCompletion; private boolean dontSetBuildResultOnFailure; private int uploadTimeout = 30; // default 30 mins + private ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.CRC32; // SDK's default /** * In-memory representation of console log level. @@ -248,6 +250,12 @@ public void setUploadTimeout(int uploadTimeout) { this.uploadTimeout = Math.max(uploadTimeout, Uploads.MIN_UPLOAD_TIMEOUT); } + @DataBoundSetter + public void setChecksumAlgorithm(ChecksumAlgorithm checksumAlgorithm) { + this.checksumAlgorithm = checksumAlgorithm != null + ? checksumAlgorithm : ChecksumAlgorithm.CRC32; + } + private void log(final PrintStream logger, final String message) { log(Level.INFO, logger, message); } @@ -333,7 +341,7 @@ public void perform(@NonNull Run run, @NonNull FilePath ws, @NonNull Launc final Map escapedMetadata = buildMetadata(envVars, entry); final List records = Lists.newArrayList(); - final List fingerprints = profile.upload(run, bucket, paths, filenames, escapedMetadata, storageClass, selRegion, entry.uploadFromSlave, entry.managedArtifacts, entry.useServerSideEncryption, entry.gzipFiles, uploadTimeout); + final List fingerprints = profile.upload(run, bucket, paths, filenames, escapedMetadata, storageClass, selRegion, entry.uploadFromSlave, entry.managedArtifacts, entry.useServerSideEncryption, entry.gzipFiles, checksumAlgorithm, uploadTimeout); for (FingerprintRecord fingerprintRecord : fingerprints) { records.add(fingerprintRecord); diff --git a/src/main/java/hudson/plugins/s3/S3Profile.java b/src/main/java/hudson/plugins/s3/S3Profile.java index 2c9a52f1..4582b410 100644 --- a/src/main/java/hudson/plugins/s3/S3Profile.java +++ b/src/main/java/hudson/plugins/s3/S3Profile.java @@ -16,11 +16,7 @@ import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; -import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; -import software.amazon.awssdk.services.s3.model.S3Object; +import software.amazon.awssdk.services.s3.model.*; import java.io.IOException; import java.util.ArrayList; @@ -163,6 +159,23 @@ public List upload(Run run, final boolean useServerSideEncryption, final boolean gzipFiles, final int uploadTimeout) throws IOException, InterruptedException { + return upload(run, bucketName, filePaths, fileNames, userMetadata, storageClass, selregion, + uploadFromSlave, managedArtifacts, useServerSideEncryption, gzipFiles, ChecksumAlgorithm.CRC32_C, uploadTimeout); + } + + public List upload(Run run, + final String bucketName, + final List filePaths, + final List fileNames, + final Map userMetadata, + final String storageClass, + final String selregion, + final boolean uploadFromSlave, + final boolean managedArtifacts, + final boolean useServerSideEncryption, + final boolean gzipFiles, + final ChecksumAlgorithm checksumAlgorithm, + final int uploadTimeout) throws IOException, InterruptedException { final List fingerprints = new ArrayList<>(fileNames.size()); try { @@ -183,10 +196,10 @@ public List upload(Run run, final MasterSlaveCallable upload; if (gzipFiles) { upload = new S3GzipCallable(accessKey, secretKey, useRole, dest, userMetadata, - storageClass, selregion, useServerSideEncryption, getProxy(), usePathStyle); + storageClass, selregion, useServerSideEncryption, getProxy(), usePathStyle, checksumAlgorithm); } else { upload = new S3UploadCallable(accessKey, secretKey, useRole, dest, userMetadata, - storageClass, selregion, useServerSideEncryption, getProxy(), usePathStyle); + storageClass, selregion, useServerSideEncryption, getProxy(), usePathStyle, checksumAlgorithm); } final FingerprintRecord fingerprintRecord = repeat(maxUploadRetries, uploadRetryTime, dest, () -> { diff --git a/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java b/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java index 4d72a4f8..8a5fccfe 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java @@ -7,6 +7,7 @@ import hudson.plugins.s3.Destination; import hudson.remoting.VirtualChannel; import hudson.util.Secret; +import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import java.io.File; @@ -15,6 +16,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; +import java.util.Objects; import java.util.function.Consumer; public abstract class S3BaseUploadCallable extends S3Callable { @@ -23,16 +25,23 @@ public abstract class S3BaseUploadCallable extends S3Callable { private final String storageClass; private final Map userMetadata; private final boolean useServerSideEncryption; - + private final ChecksumAlgorithm checksumAlgorithm; public S3BaseUploadCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy, boolean usePathStyle) { + this(accessKey, secretKey, useRole, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy, usePathStyle, null); + } + + public S3BaseUploadCallable(String accessKey, Secret secretKey, boolean useRole, + Destination dest, Map userMetadata, String storageClass, String selregion, + boolean useServerSideEncryption, ProxyConfiguration proxy, boolean usePathStyle, ChecksumAlgorithm checksumAlgorithm) { super(accessKey, secretKey, useRole, selregion, proxy, usePathStyle); this.dest = dest; this.storageClass = storageClass; this.userMetadata = userMetadata; this.useServerSideEncryption = useServerSideEncryption; + this.checksumAlgorithm = checksumAlgorithm; } /** @@ -59,6 +68,7 @@ protected Uploads.Metadata buildMetadata(FilePath filePath) throws IOException, if (useServerSideEncryption) { metadata.sseCustomerAlgorithm("AES256"); } + metadata.checksumAlgorithm(Objects.requireNonNullElse(checksumAlgorithm, ChecksumAlgorithm.CRC32)); }; Uploads.Metadata metadata = new Uploads.Metadata(builder); metadata.setContentLength(contentLength); diff --git a/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java b/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java index 68fc0eba..66eefc45 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java @@ -8,6 +8,7 @@ import hudson.plugins.s3.Uploads; import hudson.util.Secret; import org.apache.commons.io.IOUtils; +import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; import software.amazon.awssdk.transfer.s3.model.Upload; import software.amazon.awssdk.transfer.s3.progress.TransferListener; @@ -23,7 +24,11 @@ public final class S3GzipCallable extends S3BaseUploadCallable implements MasterSlaveCallable { public S3GzipCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy, boolean usePathStyle) { - super(accessKey, secretKey, useRole, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy, usePathStyle); + this(accessKey, secretKey, useRole, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy, usePathStyle, null); + } + + public S3GzipCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy, boolean usePathStyle, ChecksumAlgorithm checksumAlgorithm) { + super(accessKey, secretKey, useRole, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy, usePathStyle, checksumAlgorithm); } // Return a File containing the gzipped contents of the input file. diff --git a/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java b/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java index ad78426a..545633ea 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java @@ -6,6 +6,7 @@ import hudson.plugins.s3.MD5; import hudson.plugins.s3.Uploads; import hudson.util.Secret; +import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; import java.io.IOException; import java.util.Map; @@ -14,7 +15,11 @@ public final class S3UploadCallable extends S3BaseUploadCallable implements Mast private static final long serialVersionUID = 1L; public S3UploadCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy, boolean usePathStyle) { - super(accessKey, secretKey, useRole, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy, usePathStyle); + this(accessKey, secretKey, useRole, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy, usePathStyle, null); + } + + public S3UploadCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy, boolean usePathStyle, ChecksumAlgorithm checksumAlgorithm) { + super(accessKey, secretKey, useRole, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy, usePathStyle, checksumAlgorithm); } /** From c9a646f0b025f124413dca76a4df7df491ee363d Mon Sep 17 00:00:00 2001 From: prathamalwayscomeslast Date: Wed, 25 Mar 2026 23:15:18 +0530 Subject: [PATCH 2/7] fix: enabled setting the correct checksum algo Signed-off-by: PRATHAM ~ --- .../java/hudson/plugins/s3/S3BucketPublisher.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java index 9507397b..063face0 100644 --- a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java +++ b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java @@ -251,9 +251,18 @@ public void setUploadTimeout(int uploadTimeout) { } @DataBoundSetter - public void setChecksumAlgorithm(ChecksumAlgorithm checksumAlgorithm) { - this.checksumAlgorithm = checksumAlgorithm != null - ? checksumAlgorithm : ChecksumAlgorithm.CRC32; + public void setChecksumAlgorithm(String checksumAlgorithm) { + if (checksumAlgorithm == null) { + this.checksumAlgorithm = ChecksumAlgorithm.CRC32; + return; + } + ChecksumAlgorithm algo = ChecksumAlgorithm.fromValue(checksumAlgorithm); + if (algo == ChecksumAlgorithm.UNKNOWN_TO_SDK_VERSION) { + throw new IllegalArgumentException("Unsupported checksum algorithm: " + checksumAlgorithm); + } else if (algo == ChecksumAlgorithm.CRC64_NVME) { + throw new UnsupportedOperationException("Checksum algorithm '" + checksumAlgorithm + "' requires AWS CRT dependency, which is currently unavailable."); + } + this.checksumAlgorithm = algo; } private void log(final PrintStream logger, final String message) { From acf5fb3352639d322f67c59af3d0fab65920bcf2 Mon Sep 17 00:00:00 2001 From: prathamalwayscomeslast Date: Wed, 25 Mar 2026 23:18:01 +0530 Subject: [PATCH 3/7] better error message Signed-off-by: PRATHAM ~ --- src/main/java/hudson/plugins/s3/S3BucketPublisher.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java index 063face0..b77dddc8 100644 --- a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java +++ b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java @@ -260,7 +260,8 @@ public void setChecksumAlgorithm(String checksumAlgorithm) { if (algo == ChecksumAlgorithm.UNKNOWN_TO_SDK_VERSION) { throw new IllegalArgumentException("Unsupported checksum algorithm: " + checksumAlgorithm); } else if (algo == ChecksumAlgorithm.CRC64_NVME) { - throw new UnsupportedOperationException("Checksum algorithm '" + checksumAlgorithm + "' requires AWS CRT dependency, which is currently unavailable."); + throw new UnsupportedOperationException("Checksum algorithm '" + checksumAlgorithm + "' requires AWS CRT dependency, which is currently unavailable." + + "\nUse another algorithm (CRC32, CRC32C, SHA1, SHA256)."); } this.checksumAlgorithm = algo; } From 05c7e333c8a780a0c1f170578a2402214930f4e9 Mon Sep 17 00:00:00 2001 From: prathamalwayscomeslast Date: Wed, 25 Mar 2026 23:19:53 +0530 Subject: [PATCH 4/7] fix: handle input edge cases Signed-off-by: PRATHAM ~ --- src/main/java/hudson/plugins/s3/S3BucketPublisher.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java index b77dddc8..bf6f591a 100644 --- a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java +++ b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java @@ -50,11 +50,7 @@ import java.io.IOException; import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; @@ -256,7 +252,8 @@ public void setChecksumAlgorithm(String checksumAlgorithm) { this.checksumAlgorithm = ChecksumAlgorithm.CRC32; return; } - ChecksumAlgorithm algo = ChecksumAlgorithm.fromValue(checksumAlgorithm); + String normalized = checksumAlgorithm.toUpperCase(Locale.ROOT); + ChecksumAlgorithm algo = ChecksumAlgorithm.fromValue(normalized); if (algo == ChecksumAlgorithm.UNKNOWN_TO_SDK_VERSION) { throw new IllegalArgumentException("Unsupported checksum algorithm: " + checksumAlgorithm); } else if (algo == ChecksumAlgorithm.CRC64_NVME) { From d5a0097799490e34938d7a24f0a844482ca66694 Mon Sep 17 00:00:00 2001 From: prathamalwayscomeslast Date: Thu, 26 Mar 2026 00:24:17 +0530 Subject: [PATCH 5/7] fix: S3 profile upload mock updated with checksum algo - resolves failing test Signed-off-by: PRATHAM ~ --- src/main/java/hudson/plugins/s3/S3BucketPublisher.java | 9 +++++++-- src/test/java/hudson/plugins/s3/S3Test.java | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java index bf6f591a..5dc352b3 100644 --- a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java +++ b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java @@ -50,7 +50,12 @@ import java.io.IOException; import java.io.PrintStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; @@ -248,7 +253,7 @@ public void setUploadTimeout(int uploadTimeout) { @DataBoundSetter public void setChecksumAlgorithm(String checksumAlgorithm) { - if (checksumAlgorithm == null) { + if (checksumAlgorithm == null || checksumAlgorithm.isBlank()) { this.checksumAlgorithm = ChecksumAlgorithm.CRC32; return; } diff --git a/src/test/java/hudson/plugins/s3/S3Test.java b/src/test/java/hudson/plugins/s3/S3Test.java index daa49f51..ad734fb5 100644 --- a/src/test/java/hudson/plugins/s3/S3Test.java +++ b/src/test/java/hudson/plugins/s3/S3Test.java @@ -19,6 +19,7 @@ import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.junit.jupiter.WithJenkins; import org.mockito.Mockito; +import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; import java.io.IOException; import java.util.Collections; @@ -133,6 +134,7 @@ private S3Profile mockS3Profile(String profileName) throws IOException, Interrup Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.any(ChecksumAlgorithm.class), Mockito.anyInt() )).thenReturn(newArrayList(new FingerprintRecord(true, "bucket", "path", "eu-west-1", "xxxx"))); return profile; From b0cad8f13ff1156b60a2351910455189a3f02d80 Mon Sep 17 00:00:00 2001 From: prathamalwayscomeslast Date: Thu, 26 Mar 2026 00:58:02 +0530 Subject: [PATCH 6/7] refactor: remove wildcard import Signed-off-by: PRATHAM ~ --- src/main/java/hudson/plugins/s3/S3Profile.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/hudson/plugins/s3/S3Profile.java b/src/main/java/hudson/plugins/s3/S3Profile.java index 4582b410..d707ac30 100644 --- a/src/main/java/hudson/plugins/s3/S3Profile.java +++ b/src/main/java/hudson/plugins/s3/S3Profile.java @@ -16,7 +16,12 @@ import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.*; +import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.S3Object; import java.io.IOException; import java.util.ArrayList; From 47accf22bd3d22b41c6a480c796a0d22f584ab00 Mon Sep 17 00:00:00 2001 From: prathamalwayscomeslast Date: Thu, 26 Mar 2026 01:02:23 +0530 Subject: [PATCH 7/7] fix: propagate correct default Signed-off-by: PRATHAM ~ --- src/main/java/hudson/plugins/s3/S3Profile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/hudson/plugins/s3/S3Profile.java b/src/main/java/hudson/plugins/s3/S3Profile.java index d707ac30..f03aa0e4 100644 --- a/src/main/java/hudson/plugins/s3/S3Profile.java +++ b/src/main/java/hudson/plugins/s3/S3Profile.java @@ -165,7 +165,7 @@ public List upload(Run run, final boolean gzipFiles, final int uploadTimeout) throws IOException, InterruptedException { return upload(run, bucketName, filePaths, fileNames, userMetadata, storageClass, selregion, - uploadFromSlave, managedArtifacts, useServerSideEncryption, gzipFiles, ChecksumAlgorithm.CRC32_C, uploadTimeout); + uploadFromSlave, managedArtifacts, useServerSideEncryption, gzipFiles, null, uploadTimeout); } public List upload(Run run,