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 { RepositoryObject } from "../../types";
import type { RepositoryServiceDeps } from "./types";
const GIT_ESCAPED_JSON_FIELDS_WITH_UNESCAPED_NEWLINES_REGEXP =
/ \^@\^[a-z_]+\^@\^: \^@\^([^\^]+|)\^@\^,?/gim;
const makeGetRepositoryObject: ServiceMethodFactory<
RepositoryServiceDeps,
[Repository, string],
Promise<RepositoryObject | null>
> = ({ request }) => {
return async (repo, objectId) => {
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}".`
);
}
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}`);
}
var format =
"{%n ^@^commit^@^: ^@^%H^@^,%n ^@^abbreviated_commit^@^: ^@^%h^@^,%n ^@^tree^@^: ^@^%T^@^,%n ^@^abbreviated_tree^@^: ^@^%t^@^,%n ^@^parent^@^: ^@^%P^@^,%n ^@^abbreviated_parent^@^: ^@^%p^@^,%n ^@^refs^@^: ^@^%D^@^,%n ^@^encoding^@^: ^@^%e^@^,%n ^@^subject^@^: ^@^%s^@^,%n ^@^sanitized_subject_line^@^: ^@^%f^@^,%n ^@^body^@^: ^@^%b^@^,%n ^@^commit_notes^@^: ^@^%N^@^,%n ^@^verification_flag^@^: ^@^%G?^@^,%n ^@^signer^@^: ^@^%GS^@^,%n ^@^signer_key^@^: ^@^%GK^@^,%n ^@^author^@^: {%n ^@^name^@^: ^@^%aN^@^,%n ^@^email^@^: ^@^%aE^@^,%n ^@^date^@^: ^@^%aD^@^%n },%n ^@^commiter^@^: {%n ^@^name^@^: ^@^%cN^@^,%n ^@^email^@^: ^@^%cE^@^,%n ^@^date^@^: ^@^%cD^@^%n }%n},";
const args = [
"show",
"--quiet",
`--pretty=format:${format}`,
objectId,
].filter((x): x is string => x != null);
const gitShowObjectProcess = spawn("git", args, {
cwd: repoPath,
env: {
LANG: "C",
},
});
try {
const gitShowObjectResult = await new Promise<RepositoryObject>(
(resolve, reject) => {
let buffer = [] as string[];
gitShowObjectProcess.stdout.on("data", (data) => buffer.push(data));
gitShowObjectProcess.stderr.on("data", (data) => {
reject(new Error(Buffer.from(data).toString("utf-8")));
});
gitShowObjectProcess.stdout.on("close", () => {
let escapedJson = buffer.join("");
const fieldsToEscape = escapedJson.match(
GIT_ESCAPED_JSON_FIELDS_WITH_UNESCAPED_NEWLINES_REGEXP
);
if (fieldsToEscape != null && Array.isArray(fieldsToEscape)) {
fieldsToEscape.forEach((fieldTxt) => {
escapedJson = escapedJson.replace(
fieldTxt,
fieldTxt.split("\n").join("\\n")
);
});
}
escapedJson = escapedJson
.replace(/"/gm, '\\"')
.replace(/\^@\^/gm, '"');
escapedJson = escapedJson.substring(0, escapedJson.length - 1);
try {
resolve(JSON.parse(escapedJson));
} catch (err) {
reject(err);
}
});
}
);
return gitShowObjectResult as RepositoryObject;
} catch (err) {
console.error("Cannot get git object", objectId, err);
return null;
}
};
};
export default makeGetRepositoryObject;