diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 96f54b5d..ba8c5422 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -7,6 +7,6 @@ jobs: update_release_draft: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@v5.19.0 + - uses: release-drafter/release-drafter@v5.20.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 5a3b1d32..b41230f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .settings .classpath +.project target work *.i* diff --git a/.project b/.project index 310aeb30..9efda33f 100644 --- a/.project +++ b/.project @@ -1,29 +1,23 @@ - - - s3 - NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse. - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.maven.ide.eclipse.maven2Builder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - org.maven.ide.eclipse.maven2Nature - org.eclipse.jdt.core.javanature - - + + + s3-plugin + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/pom.xml b/pom.xml index 279de512..bd57e654 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.jenkins-ci.plugins plugin - 4.40 + 4.47 s3 @@ -69,7 +69,7 @@ org.jenkins-ci.plugins.aws-java-sdk aws-java-sdk-minimal - 1.12.80 + 1.12.287-357.vf82d85a_6eefd com.fasterxml.jackson.dataformat @@ -84,12 +84,12 @@ org.jenkins-ci.plugins copyartifact - 1.46.2 + 1.47 org.jenkins-ci.main maven-plugin - 3.16 + 3.19 commons-io @@ -101,10 +101,15 @@ org.jenkins-ci.plugins structs + + org.jenkins-ci.plugins + envinject + 2.875.v9b_9e962da_a_ec + org.testcontainers testcontainers - 1.17.1 + 1.17.3 test @@ -124,7 +129,7 @@ io.jenkins.tools.bom bom-2.289.x - 1342.v729ca_3818e88 + 1500.ve4d05cd32975 pom import @@ -136,7 +141,7 @@ org.jenkins-ci symbol-annotation - 1.17 + 1.23 diff --git a/src/main/java/hudson/plugins/s3/Destination.java b/src/main/java/hudson/plugins/s3/Destination.java index 9a2a128e..eda81008 100644 --- a/src/main/java/hudson/plugins/s3/Destination.java +++ b/src/main/java/hudson/plugins/s3/Destination.java @@ -65,4 +65,4 @@ public static Destination newFromRun(Run run, S3Artifact artifact) { return newFromRun(run, artifact.getBucket(), artifact.getName(), artifact.useFullProjectName()); } -} +} \ No newline at end of file diff --git a/src/main/java/hudson/plugins/s3/Entry.java b/src/main/java/hudson/plugins/s3/Entry.java index 63f95cc6..03a8bdde 100644 --- a/src/main/java/hudson/plugins/s3/Entry.java +++ b/src/main/java/hudson/plugins/s3/Entry.java @@ -89,12 +89,39 @@ public final class Entry implements Describable { * Metadata overrides */ public List userMetadata; + + /** + * Inject pre-signed artifacts URL after upload was completed + */ + public boolean injectUrl; + + /** + * Build variable where artifacts URLs will be stored + */ + public String buildVariable; + + /** + * @param bucket + * @param sourceFile + * @param excludedFile + * @param storageClass + * @param selectedRegion + * @param noUploadOnFailure + * @param uploadFromSlave + * @param managedArtifacts + * @param useServerSideEncryption + * @param flatten + * @param gzipFiles + * @param keepForever + * @param showDirectlyInBrowser + * @param userMetadata + */ @DataBoundConstructor public Entry(String bucket, String sourceFile, String excludedFile, String storageClass, String selectedRegion, boolean noUploadOnFailure, boolean uploadFromSlave, boolean managedArtifacts, boolean useServerSideEncryption, boolean flatten, boolean gzipFiles, boolean keepForever, - boolean showDirectlyInBrowser, List userMetadata) { + boolean showDirectlyInBrowser, boolean injectUrl, String buildVariable, List userMetadata) { this.bucket = bucket; this.sourceFile = sourceFile; this.excludedFile = excludedFile; @@ -107,10 +134,16 @@ public Entry(String bucket, String sourceFile, String excludedFile, String stora this.flatten = flatten; this.gzipFiles = gzipFiles; this.keepForever = keepForever; - this.userMetadata = userMetadata; this.showDirectlyInBrowser = showDirectlyInBrowser; + this.injectUrl = injectUrl; + if(injectUrl) { + this.buildVariable = buildVariable; + } else { + this.buildVariable = null; + } + this.userMetadata = userMetadata; } - + @Override public Descriptor getDescriptor() { return DESCRIPOR; diff --git a/src/main/java/hudson/plugins/s3/FingerprintRecord.java b/src/main/java/hudson/plugins/s3/FingerprintRecord.java index bacbfc38..400b115c 100644 --- a/src/main/java/hudson/plugins/s3/FingerprintRecord.java +++ b/src/main/java/hudson/plugins/s3/FingerprintRecord.java @@ -29,7 +29,7 @@ public FingerprintRecord(boolean produced, String bucket, String name, String re } Fingerprint addRecord(Run run) throws IOException { - final FingerprintMap map = Jenkins.getInstance().getFingerprintMap(); + final FingerprintMap map = Jenkins.get().getFingerprintMap(); return map.getOrCreate(produced ? run : null, artifact.getName(), md5sum); } diff --git a/src/main/java/hudson/plugins/s3/S3ArtifactsAction.java b/src/main/java/hudson/plugins/s3/S3ArtifactsAction.java index cacdcab1..ba709ef5 100644 --- a/src/main/java/hudson/plugins/s3/S3ArtifactsAction.java +++ b/src/main/java/hudson/plugins/s3/S3ArtifactsAction.java @@ -122,4 +122,4 @@ private String getDownloadURL(AmazonS3 client, int signedUrlExpirySeconds, Run r return client.generatePresignedUrl(request).toExternalForm(); } -} +} \ No newline at end of file diff --git a/src/main/java/hudson/plugins/s3/S3ArtifactsProjectAction.java b/src/main/java/hudson/plugins/s3/S3ArtifactsProjectAction.java index 4659ac69..1a1e3368 100644 --- a/src/main/java/hudson/plugins/s3/S3ArtifactsProjectAction.java +++ b/src/main/java/hudson/plugins/s3/S3ArtifactsProjectAction.java @@ -55,4 +55,4 @@ public String getDisplayName() { public String getUrlName() { return null; } -} +} \ No newline at end of file diff --git a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java index dc6e5a05..470137e4 100644 --- a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java +++ b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java @@ -3,7 +3,6 @@ import com.amazonaws.AmazonClientException; import com.amazonaws.regions.Region; import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3Client; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -37,6 +36,7 @@ import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.jenkinsci.Symbol; +import org.jenkinsci.plugins.envinject.EnvInjectPluginAction; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; @@ -51,11 +51,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; + public final class S3BucketPublisher extends Recorder implements SimpleBuildStep { + + public static final String S3_DEFAULT_ENDPOINT = "https://s3.amazonaws.com"; private String profileName; @Extension @@ -248,7 +252,7 @@ public void perform(@Nonnull Run run, @Nonnull FilePath ws, @Nonnull Launc try { final Map envVars = run.getEnvironment(listener); final Map record = Maps.newHashMap(); - final List artifacts = new CopyOnWriteArrayList(); + final List artifacts = new CopyOnWriteArrayList<>(); for (Entry entry : entries) { if (entry.noUploadOnFailure && Result.FAILURE.equals(run.getResult())) { @@ -301,18 +305,42 @@ public void perform(@Nonnull Run run, @Nonnull FilePath ws, @Nonnull Launc fingerprintRecord.setKeepForever(entry.keepForever); fingerprintRecord.setShowDirectlyInBrowser(entry.showDirectlyInBrowser); } - if (entry.managedArtifacts) { artifacts.addAll(fingerprints); fillFingerprints(run, listener, record, fingerprints); } + addS3ArtifactsAction(run, profile, fingerprints); + if(entry.injectUrl) { + StringBuilder links = new StringBuilder(""); + final String s3Endpoint; + if (StringUtils.isNotEmpty(ClientHelper.ENDPOINT)) { + s3Endpoint = ClientHelper.ENDPOINT; + } else { + s3Endpoint = S3_DEFAULT_ENDPOINT; + } + for (FingerprintRecord fingerprintRecord : fingerprints) { + StringBuffer sb = new StringBuffer(s3Endpoint); + sb.append("/") + .append(fingerprintRecord.getArtifact().getBucket()) + .append("/") + .append(fingerprintRecord.getArtifact().getName()) + .append("\n"); + links.append(sb.toString()); + } + EnvInjectPluginAction envInjectAction = run.getAction(EnvInjectPluginAction.class); + if (envInjectAction != null) { + Map envMap = new HashMap(); + envMap.put(entry.buildVariable, links.toString()); + Set set = run.getEnvironment(listener).keySet(); + envInjectAction.overrideAll(set, envMap); + } + } } - // don't bother adding actions if none of the artifacts are managed if (!artifacts.isEmpty()) { - addS3ArtifactsAction(run, profile, artifacts); addFingerprintAction(run, record); } + } catch (AmazonClientException|IOException e) { if (!isDontSetBuildResultOnFailure()) { e.printStackTrace(listener.error("Failed to upload files")); @@ -323,7 +351,7 @@ public void perform(@Nonnull Run run, @Nonnull FilePath ws, @Nonnull Launc } } - + private void addS3ArtifactsAction(Run run, S3Profile profile, List artifacts) { S3ArtifactsAction existingAction = run.getAction(S3ArtifactsAction.class); if (existingAction != null) { diff --git a/src/main/java/hudson/plugins/s3/S3CopyArtifact.java b/src/main/java/hudson/plugins/s3/S3CopyArtifact.java index 5a498846..dea1be19 100644 --- a/src/main/java/hudson/plugins/s3/S3CopyArtifact.java +++ b/src/main/java/hudson/plugins/s3/S3CopyArtifact.java @@ -101,7 +101,7 @@ public class S3CopyArtifact extends Builder implements SimpleBuildStep { private final Boolean flatten; private final Boolean optional; - private static final BuildSelector DEFAULT_BUILD_SELECTOR = new StatusBuildSelector(true); + private static final BuildSelector DEFAULT_BUILD_SELECTOR = new StatusBuildSelector(); @DataBoundConstructor public S3CopyArtifact(String projectName, BuildSelector buildSelector, String filter, @@ -344,8 +344,7 @@ public FormValidation doCheckProjectName( } else if (value.indexOf('$') >= 0) { result = FormValidation.warning(Messages.CopyArtifact_ParameterizedName()); - } - else { + } else { AbstractProject nearProject = AbstractProject.findNearest(value); if (nearProject != null) { result = FormValidation.error( @@ -353,7 +352,7 @@ else if (value.indexOf('$') >= 0) { value, nearProject.getName())); } else { result = FormValidation.error( - Messages.BuildTrigger_NoSuchProject(value)); + Messages.BuildTrigger_NoSuchProject(value)); } } return result; @@ -370,9 +369,9 @@ public String getDisplayName() { } public DescriptorExtensionList> getBuildSelectors() { - final DescriptorExtensionList> list = DescriptorExtensionList.createDescriptorList(Jenkins.getInstance(), BuildSelector.class); + final DescriptorExtensionList> list = DescriptorExtensionList.createDescriptorList(Jenkins.get(), BuildSelector.class); // remove from list some of the CopyArchiver build selector that we can't deal with - list.remove(WorkspaceSelector.DESCRIPTOR); + list.remove(Jenkins.get().getDescriptor()); return list; } } @@ -448,11 +447,13 @@ private void add(String projectName, int buildNumber) { } @Override - public void buildEnvVars(AbstractBuild build, EnvVars env) { + public void buildEnvironment(@Nonnull Run run, EnvVars env) { if (data!=null) { env.putAll(data); } } + + @Override public String getIconFileName() { return null; } diff --git a/src/main/java/hudson/plugins/s3/S3Profile.java b/src/main/java/hudson/plugins/s3/S3Profile.java index 6095e8da..564d612e 100644 --- a/src/main/java/hudson/plugins/s3/S3Profile.java +++ b/src/main/java/hudson/plugins/s3/S3Profile.java @@ -304,4 +304,4 @@ public String toString() { private ProxyConfiguration getProxy() { return Jenkins.get().proxy; } -} +} \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/s3/Entry/config.jelly b/src/main/resources/hudson/plugins/s3/Entry/config.jelly index 7046a7ed..4c8d4ec0 100644 --- a/src/main/resources/hudson/plugins/s3/Entry/config.jelly +++ b/src/main/resources/hudson/plugins/s3/Entry/config.jelly @@ -40,6 +40,13 @@ + + + + + + + diff --git a/src/main/resources/hudson/plugins/s3/Entry/help-buildVariable.html b/src/main/resources/hudson/plugins/s3/Entry/help-buildVariable.html new file mode 100644 index 00000000..590434c8 --- /dev/null +++ b/src/main/resources/hudson/plugins/s3/Entry/help-buildVariable.html @@ -0,0 +1,8 @@ +
+Enter Jenkins build environment variable name. Its value will contain artifacts URLs separated by new line symbol \n having following format: + +http://<s3-bucket>/<artifact-file-name>\n +... +http://<s3-bucket>/<artifact-file-name>\n + +
\ No newline at end of file diff --git a/src/main/resources/hudson/plugins/s3/Entry/help-injectUrl.html b/src/main/resources/hudson/plugins/s3/Entry/help-injectUrl.html new file mode 100644 index 00000000..97231b5a --- /dev/null +++ b/src/main/resources/hudson/plugins/s3/Entry/help-injectUrl.html @@ -0,0 +1,3 @@ +
+When enabled, this lets Jenkins inject uploaded artifacts URLs in Jenkins environment variable. In this case, the variable can be used as $VAR_NAME. +
\ No newline at end of file diff --git a/src/test/java/hudson/plugins/s3/MinIOTest.java b/src/test/java/hudson/plugins/s3/MinIOTest.java index aede518c..2771b7a0 100644 --- a/src/test/java/hudson/plugins/s3/MinIOTest.java +++ b/src/test/java/hudson/plugins/s3/MinIOTest.java @@ -146,6 +146,8 @@ private static void createAndRunPublisher(final JenkinsRule r) throws Exception false, true, false, + false, + "", Collections.emptyList())), Collections.emptyList(), true, diff --git a/src/test/java/hudson/plugins/s3/S3BucketPublisherTest.java b/src/test/java/hudson/plugins/s3/S3BucketPublisherTest.java index b634120c..b668d91d 100644 --- a/src/test/java/hudson/plugins/s3/S3BucketPublisherTest.java +++ b/src/test/java/hudson/plugins/s3/S3BucketPublisherTest.java @@ -1,7 +1,9 @@ package hudson.plugins.s3; import com.gargoylesoftware.htmlunit.HttpMethod; +import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.WebRequest; +import com.gargoylesoftware.htmlunit.WebResponse; import com.gargoylesoftware.htmlunit.util.UrlUtils; import hudson.model.Item; import hudson.security.SecurityRealm; @@ -37,6 +39,7 @@ public void testConfigExists() throws Exception { Assert.assertEquals(403, webClient.getPage(request).getWebResponse().getStatusCode()); webClient = j.createWebClient().login("alice", "alice"); + webClient.setThrowExceptionOnFailingStatusCode(false); Assert.assertEquals(200, webClient.getPage(request).getWebResponse().getStatusCode()); } } diff --git a/src/test/java/hudson/plugins/s3/S3Test.java b/src/test/java/hudson/plugins/s3/S3Test.java index 3c5038c2..8bcd80cf 100644 --- a/src/test/java/hudson/plugins/s3/S3Test.java +++ b/src/test/java/hudson/plugins/s3/S3Test.java @@ -102,7 +102,18 @@ public void dontSetBuildResultTest() throws Exception { } private Entry entryForFile(String fileName) { - return new Entry("bucket", fileName, "", "", "", false, false, true, false, false, false, false, false, null); + return new Entry("bucket", fileName, "", "", "", + false, + true, + true, + false, + false, + false, + true, + false, + false, + "", + null); } private Builder stepCreatingFile(String fileName) { @@ -114,7 +125,7 @@ private Builder stepCreatingFile(String fileName) { } private void replaceS3PluginProfile(S3Profile s3Profile) { - final Jenkins instance = Jenkins.getInstance(); + final Jenkins instance = Jenkins.get(); final DescriptorImpl s3Plugin = (DescriptorImpl) instance.getDescriptor(S3BucketPublisher.class); s3Plugin.replaceProfiles(newArrayList(s3Profile)); }