diff --git a/apps/worker/src/jobs/clone.ts b/apps/worker/src/jobs/clone.ts new file mode 100644 index 0000000..dd5d8d7 --- /dev/null +++ b/apps/worker/src/jobs/clone.ts @@ -0,0 +1,87 @@ +import { simpleGit } from "simple-git"; +import { mkdtemp, readdir, stat } from "node:fs/promises"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import type { CloneResult } from "@codeboard/shared"; + +async function countFiles(dir: string): Promise<{ files: number; lines: number }> { + let files = 0; + let lines = 0; + + const entries = await readdir(dir, { withFileTypes: true }); + for (const entry of entries) { + if (entry.name === ".git" || entry.name === "node_modules") continue; + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + const sub = await countFiles(fullPath); + files += sub.files; + lines += sub.lines; + } else { + files++; + const fileStat = await stat(fullPath); + lines += Math.ceil(fileStat.size / 40); + } + } + + return { files, lines }; +} + +export async function cloneRepository(repoUrl: string): Promise { + const tmpDir = await mkdtemp(join(tmpdir(), "codeboard-")); + const git = simpleGit(); + + await git.clone(repoUrl, tmpDir, ["--depth", "1", "--single-branch"]); + + const localGit = simpleGit(tmpDir); + const log = await localGit.log({ maxCount: 1 }); + const lastCommit = log.latest?.hash ?? "unknown"; + + const repoName = repoUrl + .replace(/\.git$/, "") + .split("/") + .slice(-1)[0] ?? "unknown"; + + const { files: totalFiles, lines: totalLines } = await countFiles(tmpDir); + + const languageCounts: Record = {}; + const extMap: Record = { + ".ts": "TypeScript", ".tsx": "TypeScript", + ".js": "JavaScript", ".jsx": "JavaScript", + ".py": "Python", ".go": "Go", + ".rs": "Rust", ".java": "Java", + ".rb": "Ruby", ".php": "PHP", + }; + + async function scanLanguages(dir: string) { + const entries = await readdir(dir, { withFileTypes: true }); + for (const entry of entries) { + if (entry.name.startsWith(".") || entry.name === "node_modules") continue; + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + await scanLanguages(fullPath); + } else { + const ext = entry.name.slice(entry.name.lastIndexOf(".")); + const lang = extMap[ext]; + if (lang) { + const fileStat = await stat(fullPath); + languageCounts[lang] = (languageCounts[lang] ?? 0) + fileStat.size; + } + } + } + } + await scanLanguages(tmpDir); + + return { + localPath: tmpDir, + metadata: { + name: repoName, + description: "", + defaultBranch: "main", + languages: languageCounts, + stars: 0, + lastCommit, + totalFiles, + totalLines, + }, + }; +}