3434import io .airlift .airline .Arguments ;
3535import io .airlift .airline .Command ;
3636import io .airlift .airline .Option ;
37+ import org .apache .commons .io .FileUtils ;
3738import org .apache .commons .lang3 .StringUtils ;
3839import org .openapitools .codegen .ClientOptInput ;
3940import org .openapitools .codegen .CodegenConfig ;
4647
4748import java .io .File ;
4849import java .io .IOException ;
50+ import java .nio .charset .StandardCharsets ;
4951import java .nio .file .Files ;
5052import java .nio .file .Path ;
5153import java .nio .file .Paths ;
@@ -77,6 +79,9 @@ public class GenerateBatch extends OpenApiGeneratorCommand {
7779 @ Option (name = {"--fail-fast" }, description = "fail fast on any errors" )
7880 private Boolean failFast ;
7981
82+ @ Option (name = {"--clean" }, description = "clean output of previously written files before generation" )
83+ private Boolean clean ;
84+
8085 @ Option (name = {"--timeout" }, description = "execution timeout (minutes)" )
8186 private Integer timeout ;
8287
@@ -149,7 +154,10 @@ public void execute() {
149154 ExecutorService executor = Executors .newFixedThreadPool (numThreads );
150155
151156 // Execute each configurator on a separate pooled thread.
152- configurators .forEach (configurator -> executor .execute (new GenerationRunner (configurator , rootDir , Boolean .TRUE .equals (failFast ))));
157+ configurators .forEach (configurator -> {
158+ GenerationRunner runner = new GenerationRunner (configurator , rootDir , Boolean .TRUE .equals (failFast ), Boolean .TRUE .equals (clean ));
159+ executor .execute (runner );
160+ });
153161
154162 executor .shutdown ();
155163
@@ -178,11 +186,13 @@ private static class GenerationRunner implements Runnable {
178186 private final CodegenConfigurator configurator ;
179187 private final Path rootDir ;
180188 private final boolean exitOnError ;
189+ private final boolean clean ;
181190
182- private GenerationRunner (CodegenConfigurator configurator , Path rootDir , boolean failFast ) {
191+ private GenerationRunner (CodegenConfigurator configurator , Path rootDir , boolean failFast , boolean clean ) {
183192 this .configurator = configurator ;
184193 this .rootDir = rootDir ;
185194 this .exitOnError = failFast ;
195+ this .clean = clean ;
186196 }
187197
188198 /**
@@ -198,7 +208,7 @@ private GenerationRunner(CodegenConfigurator configurator, Path rootDir, boolean
198208 */
199209 @ Override
200210 public void run () {
201- String name = "" ;
211+ String name = null ;
202212 try {
203213 GlobalSettings .reset ();
204214
@@ -210,6 +220,10 @@ public void run() {
210220 Path updated = rootDir .resolve (target );
211221 config .setOutputDir (updated .toString ());
212222
223+ if (this .clean ) {
224+ cleanPreviousFiles (name , updated );
225+ }
226+
213227 System .out .printf (Locale .ROOT , "[%s] Generating %s (outputs to %s)…%n" , Thread .currentThread ().getName (), name , updated .toString ());
214228
215229 DefaultGenerator defaultGenerator = new DefaultGenerator ();
@@ -234,6 +248,28 @@ public void run() {
234248 GlobalSettings .reset ();
235249 }
236250 }
251+
252+ private void cleanPreviousFiles (final String name , Path outDir ) throws IOException {
253+ System .out .printf (Locale .ROOT , "[%s] Cleaning previous contents for %s in %s…%n" , Thread .currentThread ().getName (), name , outDir .toString ());
254+ Path filesMeta = Paths .get (outDir .toAbsolutePath ().toString (), ".openapi-generator" , "FILES" );
255+ if (filesMeta .toFile ().exists ()) {
256+ FileUtils .readLines (filesMeta .toFile (), StandardCharsets .UTF_8 ).forEach (relativePath -> {
257+ if (!StringUtils .startsWith (relativePath , "." )) {
258+ Path file = outDir .resolve (relativePath ).toAbsolutePath ();
259+ // hack: disallow directory traversal outside of output directory. we don't want to delete wrong files.
260+ if (file .toString ().startsWith (outDir .toAbsolutePath ().toString ())) {
261+ try {
262+ Files .delete (file );
263+ } catch (Throwable e ) {
264+ System .out .printf (Locale .ROOT , "[%s] Generator %s failed to clean file %s…%n" , Thread .currentThread ().getName (), name , file );
265+ }
266+ }
267+ } else {
268+ System .out .printf (Locale .ROOT , "[%s] Generator %s skip cleaning special filename %s…%n" , Thread .currentThread ().getName (), name , relativePath );
269+ }
270+ });
271+ }
272+ }
237273 }
238274
239275 static SimpleModule getCustomDeserializationModel (final File includesDir ) {
0 commit comments