更新了代码仓库清单

This commit is contained in:
张家训
2026-06-10 14:10:56 +08:00
parent 4f8cd45b39
commit d3f37fa3db
3 changed files with 73 additions and 52 deletions
@@ -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) {