You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

72 lines
2.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const { execFileSync } = require('child_process');
const {
resolveCloneUrlForGit,
stripGitCredentials,
buildGitHttpAuthConfigArgs,
shouldApplyDefaultGitAuth,
} = require('./gitCloneUrl.cjs');
function gitEnv() {
return {
...process.env,
GIT_TERMINAL_PROMPT: '0',
};
}
function sha16(s) {
return crypto.createHash('sha256').update(String(s)).digest('hex').slice(0, 16);
}
/**
* 浅克隆学员仓库到 .cache/student-repos/<hash>/ ,已存在则 git pull。
* @param {string} gitUrl 完整 https 地址(私有库可在 URL 中带 token
* @param {string} rootDir 项目根
* @returns {string} 本地克隆目录绝对路径
*/
function ensureStudentRepo(gitUrl, rootDir) {
const publicUrl = String(gitUrl).trim();
const authCfg = buildGitHttpAuthConfigArgs(publicUrl);
/** 使用 extraHeader 时不要用带密码的 origin URL避免被凭证助手改写后仍无法拉取 */
const originUrlForRemote = shouldApplyDefaultGitAuth(publicUrl)
? stripGitCredentials(publicUrl)
: resolveCloneUrlForGit(publicUrl);
const cacheRoot = path.join(rootDir, '.cache', 'student-repos');
fs.mkdirSync(cacheRoot, { recursive: true });
const dir = path.join(cacheRoot, sha16(stripGitCredentials(publicUrl)));
if (fs.existsSync(path.join(dir, '.git'))) {
try {
execFileSync('git', ['-C', dir, 'remote', 'set-url', 'origin', originUrlForRemote], {
stdio: 'pipe',
env: gitEnv(),
});
execFileSync(
'git',
[...authCfg, '-C', dir, 'pull', '--ff-only'],
{ stdio: 'pipe', timeout: 300000, env: gitEnv() },
);
} catch {
fs.rmSync(dir, { recursive: true, force: true });
execFileSync(
'git',
[...authCfg, 'clone', '--depth', '1', originUrlForRemote, dir],
{ stdio: 'pipe', timeout: 300000, env: gitEnv() },
);
}
} else {
execFileSync(
'git',
[...authCfg, 'clone', '--depth', '1', originUrlForRemote, dir],
{ stdio: 'pipe', timeout: 300000, env: gitEnv() },
);
}
if (!fs.existsSync(path.join(dir, '.git'))) {
throw new Error('git clone 后仍未发现 .git请检查仓库地址与网络');
}
return dir;
}
module.exports = { ensureStudentRepo, sha16 };