Make the build pipeline more resilient for Windows and macOS as those tend to have...
[SquirrelJME.git] / tools / cicd-release-bundle / src / main / java / cc / squirreljme / cicd / FossilCommand.java
blob0982ba5b3b22f6e682a5f86ac318ceb077eafcc7
1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // Multi-Phasic Applications: SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // ---------------------------------------------------------------------------
10 package cc.squirreljme.cicd;
12 import java.io.File;
13 import java.io.IOException;
14 import java.nio.file.Files;
15 import java.nio.file.Path;
16 import java.nio.file.Paths;
17 import java.nio.file.StandardOpenOption;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.List;
21 import java.util.regex.Pattern;
23 /**
24 * Access to the Fossil SCM command line interface.
26 * @since 2024/10/05
28 public class FossilCommand
30 /** Retry this many times. */
31 private static final int RETRY_COUNT =
32 40;
34 /** Time to wait before a retry. */
35 private static final int RETRY_DELAY =
36 1500;
38 /** The executable path. */
39 protected final Path exe;
41 /**
42 * Initializes the command executor.
44 * @param __exe The executable to use.
45 * @throws NullPointerException On null arguments.
46 * @since 2024/10/05
48 public FossilCommand(Path __exe)
49 throws NullPointerException
51 if (__exe == null)
52 throw new NullPointerException("NARG");
54 this.exe = __exe;
57 /**
58 * Adds a file to the un-versioned space.
60 * @param __path The file to add.
61 * @param __target The target destination.
62 * @throws IOException On read/write errors.
63 * @throws NullPointerException On null arguments.
64 * @since 2024/10/05
66 public void add(Path __path, String __target)
67 throws IOException, NullPointerException
69 if (__path == null || __target == null)
70 throw new NullPointerException("NARG");
72 this.exec("uv", "add",
73 __path.toAbsolutePath().toString(), "--as", __target);
76 /**
77 * Adds a file to the un-versioned space.
79 * @param __rawData The raw file data to add.
80 * @param __target The target destination.
81 * @throws IOException On read/write errors.
82 * @throws NullPointerException On null arguments.
83 * @since 2024/10/05
85 public void add(byte[] __rawData, String __target)
86 throws IOException, NullPointerException
88 if (__rawData == null || __target == null)
89 throw new NullPointerException("NARG");
91 Path temp = null;
92 try
94 // Setup temporary file
95 temp = Files.createTempFile("squirreljme", ".bin");
97 // Write everything there
98 Files.write(temp, __rawData,
99 StandardOpenOption.WRITE,
100 StandardOpenOption.TRUNCATE_EXISTING,
101 StandardOpenOption.CREATE);
103 // Store it
104 this.add(temp.toAbsolutePath(), __target);
106 finally
108 if (temp != null)
111 Files.delete(temp);
113 catch (IOException __ignored)
120 * Executes the fossil command.
122 * @param __args The arguments to use.
123 * @throws IOException On read/write errors.
124 * @throws NullPointerException On null arguments.
125 * @since 2024/10/05
127 public void exec(String... __args)
128 throws IOException, NullPointerException
130 if (__args == null)
131 throw new NullPointerException("NARG");
133 // Add in all arguments
134 List<String> args = new ArrayList<>();
135 args.add(this.exe.toAbsolutePath().toString());
136 args.addAll(Arrays.asList(__args));
138 // Run multiple times, in the event the database is locked...
139 List<IOException> fails = new ArrayList<>();
140 for (int i = 0; i < FossilCommand.RETRY_COUNT; i++)
143 // Setup process
144 ProcessBuilder builder = new ProcessBuilder(args);
145 builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
146 builder.redirectError(ProcessBuilder.Redirect.INHERIT);
148 // Start and wait for it to complete
149 Process process = builder.start();
151 // Wait for completion, if successful then stop
152 int exit = process.waitFor();
153 if (exit == 0)
154 return;
156 // Mark failure
157 fails.add(new IOException("Exited with " + exit));
159 // Wait a bit before running it again
160 Thread.sleep(FossilCommand.RETRY_DELAY);
162 catch (InterruptedException __e)
164 throw new IOException("Interrupted", __e);
167 // Completely failed
168 IOException toss = new IOException("Failed to run: " + args);
169 for (IOException fail : fails)
170 toss.addSuppressed(fail);
171 throw toss;
175 * Finds the Fossil command.
177 * @return The resultant command or {@code null} if there is none.
178 * @since 2024/10/05
180 public static FossilCommand instance()
182 // Windows or not?
183 String exeName;
184 if (System.getProperty("os.name").toLowerCase().contains("windows"))
185 exeName = "fossil.exe";
186 else
187 exeName = "fossil";
189 // Use system PATH
190 String paths = System.getenv("PATH");
191 if (paths != null)
192 for (String path : paths.split(Pattern.quote(File.pathSeparator)))
194 Path maybe = Paths.get(path, exeName);
195 if (Files.exists(maybe) && Files.isExecutable(maybe))
196 return new FossilCommand(maybe);
199 // Not found
200 return null;