import { existsSync } from "node:fs";
import { spawn } from "node:child_process";
import type { ServiceMethodFactory } from "@ethicdevs/react-monolith";
import type { Repository } from "@prisma/client";
import { Env } from "../../env";
import type { RepositoryFile } from "../../types";
import type { RepositoryServiceDeps } from "./types";
const GIT_LS_TREE_REGEXP =
/^([\d]+)[\s]+(blob|tree)[\s]+([a-z0-9]+)[\s]+(.*)$/i;
const makeGetRepositoryFiles: ServiceMethodFactory<
RepositoryServiceDeps,
[Repository, string | undefined, string | undefined],
Promise<RepositoryFile[]>
> = ({ request }) => {
return async (repo, path = "", ref = "HEAD") => {
const parentOrg = await request.prisma.organization.findUnique({
where: {
id: repo.organizationId,
},
});
if (parentOrg == null) {
throw new Error(
`Could not find the parent organization for project "${repo.slug}".`
);
}
try {
const repoPath = `${Env.GIT_REPOSITORIES_ROOT}/${parentOrg.slug}/${repo.slug}.git`;
if (existsSync(repoPath) === false) {
throw new Error(
`Could not find a valid git repository at: ${repoPath}`
);
}
const gitLsTreeProcess = spawn("git", ["ls-tree", `${ref}:${path}`], {
cwd: repoPath,
});
const gitLsTreeResult = await new Promise<string>((resolve, reject) => {
let buffer = [] as string[];
gitLsTreeProcess.stdout.on("data", (data) => buffer.push(data));
gitLsTreeProcess.stderr.on("data", (data) => {
reject(new Error(Buffer.from(data).toString("utf-8")));
});
gitLsTreeProcess.stdout.on("close", () => {
resolve(buffer.join(""));
});
});
const repoFiles = gitLsTreeResult
.split("\n")
.map((line) => {
const matches = GIT_LS_TREE_REGEXP.exec(line);
if (matches == null || Array.isArray(matches) === false) {
return null;
}
const [_, permissions, type, id, name] = matches;
return {
id,
name,
permissions,
type,
} as RepositoryFile;
})
.filter((x): x is RepositoryFile => x != null)
.sort((a, b) => {
if (a.type === "blob" && b.type === "tree") {
return 1;
} else if (a.type === "blob" && b.type === "blob") {
return 0;
} else if (a.type === "tree" && b.type === "tree") {
return 0;
} else {
return -1;
}
});
return repoFiles;
} catch (_) {
return [];
}
};
};
export default makeGetRepositoryFiles;