diff --git a/rewrite-core/src/main/java/org/openrewrite/shell/exec/ShellExecutor.java b/rewrite-core/src/main/java/org/openrewrite/shell/exec/ShellExecutor.java new file mode 100644 index 000000000000..663815b5c13c --- /dev/null +++ b/rewrite-core/src/main/java/org/openrewrite/shell/exec/ShellExecutor.java @@ -0,0 +1,80 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.shell.exec; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.scheduling.WorkingDirectoryExecutionContextView; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@org.openrewrite.Incubating(since = "8.30.0") +public interface ShellExecutor { + + @SuppressWarnings("unused") + default void init() {} + + @SuppressWarnings("unused") + default Path exec(List command, Path workingDirectory, Map environment, ExecutionContext ctx) { + return exec(command, workingDirectory, environment, ctx, 5L); + } + + default Path exec(List command, Path workingDirectory, Map environment, ExecutionContext ctx, Long timeoutMinutes) { + Path stdOut = null, stdErr = null; + try { + ProcessBuilder builder = new ProcessBuilder(); + builder.command(command); + builder.directory(workingDirectory.toFile()); + builder.environment().putAll(environment); + + stdOut = Files.createTempFile(WorkingDirectoryExecutionContextView.view(ctx).getWorkingDirectory(), "shell", + null); + stdErr = Files.createTempFile(WorkingDirectoryExecutionContextView.view(ctx).getWorkingDirectory(), "shell", + null); + builder.redirectOutput(ProcessBuilder.Redirect.to(stdOut.toFile())); + builder.redirectError(ProcessBuilder.Redirect.to(stdErr.toFile())); + Process process = builder.start(); + + process.waitFor(timeoutMinutes, TimeUnit.MINUTES); + if (process.exitValue() != 0) { + String error = "Command failed:" + String.join(" ", command); + if (Files.exists(stdErr)) { + error += "\n" + new String(Files.readAllBytes(stdErr)); + } + throw new RuntimeException(error); + } + return stdOut; + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } finally { + if (stdOut != null) { + // noinspection ResultOfMethodCallIgnored + stdOut.toFile().delete(); + } + if (stdErr != null) { + // noinspection ResultOfMethodCallIgnored + stdErr.toFile().delete(); + } + } + } + + @SuppressWarnings("unused") + default void postExec() {} +} \ No newline at end of file