3434import java .io .PrintStream ;
3535import java .io .Serial ;
3636import java .io .Serializable ;
37+ import java .nio .file .Path ;
38+ import java .nio .file .Paths ;
3739import java .util .ArrayList ;
3840import java .util .Collection ;
41+ import java .util .HashMap ;
42+ import java .util .Map ;
3943
4044import jakarta .servlet .ServletException ;
4145
4751import edu .umd .cs .findbugs .annotations .NonNull ;
4852import edu .umd .cs .findbugs .annotations .SuppressFBWarnings ;
4953
54+ import jenkins .model .ArtifactManager ;
55+
5056public class RobotPublisher extends Recorder implements Serializable ,
5157 MatrixAggregatable , SimpleBuildStep {
5258
@@ -71,6 +77,7 @@ public class RobotPublisher extends Recorder implements Serializable,
7177 private String [] otherFiles ;
7278 final private String overwriteXAxisLabel ;
7379 final private boolean enableCache ;
80+ final private boolean useArtifactManager ;
7481
7582 //Default to true
7683 private boolean countSkippedTests = false ;
@@ -88,12 +95,13 @@ public class RobotPublisher extends Recorder implements Serializable,
8895 * @param unstableThreshold Threshold of test pass percentage for unstable builds
8996 * @param otherFiles Other files to be saved
9097 * @param enableCache True if caching is used
98+ * @param useArtifactManager True if Artifact Manager is used
9199 */
92100 @ DataBoundConstructor
93101 public RobotPublisher (String archiveDirName , String outputPath , String outputFileName ,
94102 boolean disableArchiveOutput , String reportFileName , String logFileName ,
95103 double passThreshold , double unstableThreshold ,
96- boolean countSkippedTests , String otherFiles , boolean enableCache , String overwriteXAxisLabel ) {
104+ boolean countSkippedTests , String otherFiles , boolean enableCache , String overwriteXAxisLabel , boolean useArtifactManager ) {
97105 this .archiveDirName = archiveDirName ;
98106 this .outputPath = outputPath ;
99107 this .outputFileName = outputFileName ;
@@ -105,6 +113,7 @@ public RobotPublisher(String archiveDirName, String outputPath, String outputFil
105113 this .countSkippedTests = countSkippedTests ;
106114 this .enableCache = enableCache ;
107115 this .overwriteXAxisLabel = overwriteXAxisLabel ;
116+ this .useArtifactManager = useArtifactManager ;
108117
109118 if (otherFiles != null ) {
110119 String [] filemasks = otherFiles .split ("," );
@@ -235,6 +244,15 @@ public String getOverwriteXAxisLabel() {
235244 return overwriteXAxisLabel ;
236245 }
237246
247+ /**
248+ * Gets value of useArtifactManager
249+ *
250+ * @return true if Artifact Manager is used
251+ */
252+ public boolean getUseArtifactManager () {
253+ return useArtifactManager ;
254+ }
255+
238256 /**
239257 * {@inheritDoc}
240258 */
@@ -286,25 +304,26 @@ public void perform(Run<?, ?> build, @NonNull FilePath workspace, @NonNull EnvVa
286304
287305 if (!DEFAULT_JENKINS_ARCHIVE_DIR .equalsIgnoreCase (getArchiveDirName ())) {
288306 logger .println (Messages .robot_publisher_copying ());
289- //Save configured Robot files (including split output) to build dir
290- copyFilesToBuildDir (build , workspace , expandedOutputPath , StringUtils .join (modifyMasksforSplittedOutput (new String []{expandedReportFileName , expandedLogFileName , logFileJavascripts }), "," ));
307+ //Save configured Robot files (including split output) to destination dir
308+ copyFilesToDestination (build , workspace , expandedOutputPath , StringUtils .join (modifyMasksforSplittedOutput (new String []{expandedReportFileName , expandedLogFileName , logFileJavascripts }), "," ), launcher , listener );
291309
292310 if (!getDisableArchiveOutput ()) {
293- copyFilesToBuildDir (build , workspace , expandedOutputPath , StringUtils .join (modifyMasksforSplittedOutput (new String []{expandedOutputFileName }), "," ));
311+ copyFilesToDestination (build , workspace , expandedOutputPath , StringUtils .join (modifyMasksforSplittedOutput (new String []{expandedOutputFileName }), "," ), launcher , listener );
294312 }
295313
296- //Save other configured files to build dir
314+ //Save other configured files to destination dir
297315 if (StringUtils .isNotBlank (getOtherFiles ())) {
298316 String filemask = buildEnv .expand (getOtherFiles ());
299- copyFilesToBuildDir (build , workspace , expandedOutputPath , filemask );
317+ copyFilesToDestination (build , workspace , expandedOutputPath , filemask , launcher , listener );
300318 }
301319 logger .println (Messages .robot_publisher_done ());
302320 }
303321
304322 logger .println (Messages .robot_publisher_assigning ());
305323
306324 String label = buildEnv .expand (overwriteXAxisLabel );
307- RobotBuildAction action = new RobotBuildAction (build , result , getArchiveDirName (), listener , expandedReportFileName , expandedLogFileName , enableCache , label , countSkippedTests );
325+ RobotBuildAction action = new RobotBuildAction (build , result , getArchiveDirName (), listener ,
326+ expandedReportFileName , expandedLogFileName , enableCache , label , countSkippedTests , useArtifactManager );
308327 build .addAction (action );
309328
310329 // set RobotProjectAction as project action
@@ -339,7 +358,29 @@ public void perform(Run<?, ?> build, @NonNull FilePath workspace, @NonNull EnvVa
339358 }
340359
341360 /**
342- * Copy files with given filemasks from input path relative to build into specific build file archive dir
361+ * Copy files with given filemasks from input path relative to build into
362+ * local build archive dir or artifact manager destination
363+ *
364+ * @param build The Jenkins run
365+ * @param workspace Build workspace
366+ * @param inputPath Base path for copy. Relative to build workspace.
367+ * @param filemaskToCopy List of Ant GLOB style filemasks to copy from dirs specified at inputPathMask
368+ * @param launcher A way to start processes
369+ * @param listener A place to send output
370+ * @throws IOException thrown exception
371+ * @throws InterruptedException thrown exception
372+ */
373+ public void copyFilesToDestination (Run <?, ?> build , FilePath workspace , String inputPath , String filemaskToCopy ,
374+ Launcher launcher , TaskListener listener ) throws IOException , InterruptedException {
375+ if (getUseArtifactManager ()) {
376+ archiveFilesToDestination (build , workspace , inputPath , filemaskToCopy , launcher , listener );
377+ } else {
378+ copyFilesToBuildDir (build , workspace , inputPath , filemaskToCopy );
379+ }
380+ }
381+
382+ /**
383+ * Copy files with given filemasks from input path relative to build into local build file archive dir
343384 *
344385 * @param build The Jenkins run
345386 * @param inputPath Base path for copy. Relative to build workspace.
@@ -351,11 +392,55 @@ public void perform(Run<?, ?> build, @NonNull FilePath workspace, @NonNull EnvVa
351392 public void copyFilesToBuildDir (Run <?, ?> build , FilePath workspace ,
352393 String inputPath , String filemaskToCopy ) throws IOException , InterruptedException {
353394 FilePath srcDir = new FilePath (workspace , inputPath );
354- FilePath destDir = new FilePath (new FilePath (build .getRootDir ()),
355- getArchiveDirName ());
395+ FilePath destDir = new FilePath (new FilePath (build .getRootDir ()), getArchiveDirName ());
356396 srcDir .copyRecursiveTo (filemaskToCopy , destDir );
357397 }
358398
399+ /**
400+ * Copy files with given filemasks from input path relative to Artifact Manager
401+ *
402+ * @param build The Jenkins run
403+ * @param workspace Build workspace
404+ * @param inputPath Base path for copy. Relative to build workspace.
405+ * @param artifactsFilemask List of Ant GLOB style filemasks to copy from dirs specified at inputPathMask
406+ * @param launcher A way to start processes
407+ * @param listener A place to send output
408+ * @throws IOException thrown exception
409+ * @throws InterruptedException thrown exception
410+ */
411+ public void archiveFilesToDestination (Run <?, ?> build , FilePath workspace , String inputPath , String artifactsFilemask ,
412+ Launcher launcher , TaskListener listener ) throws IOException , InterruptedException {
413+ FilePath srcDir = new FilePath (workspace , inputPath );
414+ FilePath [] artifactFiles = srcDir .list (artifactsFilemask );
415+
416+ Map <String , String > artifacts = new HashMap <>();
417+ for (FilePath file : artifactFiles ) {
418+ // Use relative path as artifact name
419+ String pathInArchiveArea = getRelativePath (srcDir , file );
420+ String pathInWorkspaceArea = getRelativePath (workspace , file );
421+ artifacts .put (pathInArchiveArea , pathInWorkspaceArea );
422+ }
423+
424+ // This will automatically use the configured artifact manager (S3, etc.)
425+ ArtifactManager artifactManager = build .pickArtifactManager ();
426+ artifactManager .archive (srcDir , launcher , (BuildListener )listener , artifacts );
427+ if (artifacts .isEmpty ()) {
428+ listener .getLogger ().println ("No artifacts to archive" );
429+ } else {
430+ for (Map .Entry <String ,String > artifact : artifacts .entrySet ()) {
431+ listener .getLogger ().println ("The artifact to archive: " + artifact .getKey () + "->" + artifact .getValue ());
432+ }
433+ }
434+ }
435+
436+ private String getRelativePath (FilePath path1 , FilePath path2 ) {
437+ Path javaPath1 = Paths .get (path1 .getRemote ());
438+ Path javaPath2 = Paths .get (path2 .getRemote ());
439+ Path relativeJavaPath = javaPath1 .relativize (javaPath2 );
440+
441+ return relativeJavaPath .toString ();
442+ }
443+
359444 /**
360445 * Return filename without file suffix.
361446 *
0 commit comments