更新了代码仓库清单
This commit is contained in:
@@ -20,10 +20,14 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@@ -55,6 +59,7 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
||||
|
||||
private static final String DEFAULT_BRANCH = "master";
|
||||
private static final long CMD_TIMEOUT_SECONDS = 120;
|
||||
private static final long SYNC_TOTAL_TIMEOUT_MINUTES = 30; // 整体同步超时
|
||||
private static final int PARALLEL = 6; // 并发数
|
||||
|
||||
private final FileOperationHelper fileOperationHelper;
|
||||
@@ -214,8 +219,11 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
||||
final String repoPath = proj.getPath();
|
||||
final String repoName = extractName(repoPath);
|
||||
final String targetBranch = branch;
|
||||
final int taskNum = i + 1; // 固定序号,用于定位卡住的项
|
||||
|
||||
pool.submit(() -> {
|
||||
// 开始处理时立即打印,方便定位卡住的项目
|
||||
System.out.printf("[%d/%d] 处理中 %s ...%n", taskNum, total, repoName);
|
||||
try {
|
||||
SyncResult result = syncOneRepo(repoPath, targetBranch, baseUrlMap);
|
||||
resultMap.put(repoPath, result);
|
||||
@@ -226,13 +234,13 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
||||
case DAMAGED: tag = "损坏"; break;
|
||||
default: tag = "失败"; break;
|
||||
}
|
||||
System.out.printf("[%d/%d] %s >> %s%n", done, total, repoName, tag);
|
||||
System.out.printf("[%d/%d] %s >> %s (完成 %d/%d)%n", taskNum, total, repoName, tag, done, total);
|
||||
} catch (Exception e) {
|
||||
SyncResult r = SyncResult.FAILED;
|
||||
r.error = e.getMessage();
|
||||
resultMap.put(repoPath, r);
|
||||
int done = completed.incrementAndGet();
|
||||
System.out.printf("[%d/%d] %s >> 异常: %s%n", done, total, repoName, e.getMessage());
|
||||
System.out.printf("[%d/%d] %s >> 异常: %s (完成 %d/%d)%n", taskNum, total, repoName, e.getMessage(), done, total);
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
@@ -241,7 +249,12 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
||||
|
||||
pool.shutdown();
|
||||
try {
|
||||
latch.await();
|
||||
boolean allDone = latch.await(SYNC_TOTAL_TIMEOUT_MINUTES, TimeUnit.MINUTES);
|
||||
if (!allDone) {
|
||||
pool.shutdownNow();
|
||||
System.err.println("\n!!! 同步超时(超过 " + SYNC_TOTAL_TIMEOUT_MINUTES + " 分钟),强制终止剩余任务 !!!");
|
||||
// 未完成的任务不再等待,继续输出已有结果
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
pool.shutdownNow();
|
||||
@@ -522,49 +535,71 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
||||
// ==================== Git 命令执行 ====================
|
||||
|
||||
private CmdResult runGit(String workingDir, String command) {
|
||||
CmdResult result = new CmdResult();
|
||||
try {
|
||||
ProcessBuilder pb = isWindows()
|
||||
? new ProcessBuilder("cmd.exe", "/c", command)
|
||||
: new ProcessBuilder("bash", "-c", command);
|
||||
// 用 Future 包一层,防止 readLine() 因进程卡死而永久阻塞
|
||||
ExecutorService singleExecutor = Executors.newSingleThreadExecutor();
|
||||
Future<CmdResult> future = singleExecutor.submit(() -> {
|
||||
CmdResult result = new CmdResult();
|
||||
try {
|
||||
ProcessBuilder pb = isWindows()
|
||||
? new ProcessBuilder("cmd.exe", "/c", command)
|
||||
: new ProcessBuilder("bash", "-c", command);
|
||||
|
||||
if (workingDir != null) {
|
||||
pb.directory(new File(workingDir));
|
||||
}
|
||||
pb.redirectErrorStream(true);
|
||||
|
||||
Process process = pb.start();
|
||||
StringBuilder output = new StringBuilder();
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream(), java.nio.charset.StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
output.append(line).append("\n");
|
||||
if (workingDir != null) {
|
||||
pb.directory(new File(workingDir));
|
||||
}
|
||||
}
|
||||
pb.redirectErrorStream(true);
|
||||
|
||||
boolean finished = process.waitFor(CMD_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
if (!finished) {
|
||||
process.destroyForcibly();
|
||||
Process process = pb.start();
|
||||
StringBuilder output = new StringBuilder();
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream(), java.nio.charset.StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
output.append(line).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
boolean finished = process.waitFor(CMD_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
if (!finished) {
|
||||
process.destroyForcibly();
|
||||
result.ok = false;
|
||||
result.error = "命令超时(" + CMD_TIMEOUT_SECONDS + "s)";
|
||||
return result;
|
||||
}
|
||||
|
||||
int exitCode = process.exitValue();
|
||||
result.ok = (exitCode == 0);
|
||||
result.output = output.toString().trim();
|
||||
if (!result.ok) {
|
||||
String detail = output.toString().trim();
|
||||
result.error = detail.isEmpty()
|
||||
? "exit code " + exitCode
|
||||
: "exit code " + exitCode + ": " + detail;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
result.ok = false;
|
||||
result.error = "命令超时";
|
||||
return result;
|
||||
result.error = e.getMessage();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
int exitCode = process.exitValue();
|
||||
result.ok = (exitCode == 0);
|
||||
result.output = output.toString().trim();
|
||||
if (!result.ok) {
|
||||
String detail = output.toString().trim();
|
||||
result.error = detail.isEmpty()
|
||||
? "exit code " + exitCode
|
||||
: "exit code " + exitCode + ": " + detail;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
// 整体超时比内部 waitFor 多给 10 秒缓冲
|
||||
return future.get(CMD_TIMEOUT_SECONDS + 10, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
future.cancel(true);
|
||||
CmdResult result = new CmdResult();
|
||||
result.ok = false;
|
||||
result.error = e.getMessage();
|
||||
result.error = "命令超时(" + (CMD_TIMEOUT_SECONDS + 10) + "s,进程被强制终止)";
|
||||
return result;
|
||||
} catch (InterruptedException | ExecutionException | CancellationException e) {
|
||||
CmdResult result = new CmdResult();
|
||||
result.ok = false;
|
||||
result.error = "执行异常: " + e.getMessage();
|
||||
return result;
|
||||
} finally {
|
||||
singleExecutor.shutdownNow();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getRemoteUrl(String repoPath, Map<String, String> baseUrlMap) {
|
||||
|
||||
Reference in New Issue
Block a user