import { existsSync } from "node:fs";
import { spawn } from "node:child_process";
import { ServiceMethodFactory } from "@ethicdevs/react-monolith";
import parseDiff from "diffparser";
import type { Repository } from "@prisma/client";
import type { RepositoryFileDiff } from "../../types";
import { Env } from "../../env";
import type { RepositoryServiceDeps } from "./types";
import { default as makeGetRepositoryBranches } from "./getRepositoryBranches";
const makeGetRepositoryRemoteRefDiff: ServiceMethodFactory<
RepositoryServiceDeps,
[Repository, string, Repository, string],
Promise<RepositoryFileDiff[]>
> = ({ request }) => {
const getRepositoryBranches = makeGetRepositoryBranches({ request });
return async (sourceRepo, sourceFromBranch, targetRepo, targetDestBranch) => {
const sourceParentOrg = await request.prisma.organization.findUnique({
where: {
id: sourceRepo.organizationId,
},
});
if (sourceParentOrg == null) {
throw new Error(
`Could not find the parent organization for project "${sourceRepo.id}".`
);
}
const sourceBranches = await getRepositoryBranches(sourceRepo, true);
if (
sourceBranches.length <= 0 ||
sourceBranches.includes(sourceFromBranch) === false
) {
throw new Error(
`No such branch ${sourceFromBranch} found in repository "${sourceRepo.id}"`
);
}
const sourceRepoPath = `${Env.GIT_REPOSITORIES_ROOT}/${sourceParentOrg.slug}/${sourceRepo.slug}.git`;
if (existsSync(sourceRepoPath) === false) {
throw new Error(
`Could not find a valid source git repository at: ${sourceRepoPath}`
);
}
const targetParentOrg = await request.prisma.organization.findUnique({
where: {
id: targetRepo.organizationId,
},
});
if (targetParentOrg == null) {
throw new Error(
`Could not find the parent organization for project "${targetRepo.slug}".`
);
}
const targetBranches = await getRepositoryBranches(targetRepo, true);
if (
targetBranches.length <= 0 ||
targetBranches.includes(targetDestBranch) === false
) {
throw new Error(
`No such branch ${targetDestBranch} found in repository "${targetRepo.id}"`
);
}
const targetRepoPath = `${Env.GIT_REPOSITORIES_ROOT}/${targetParentOrg.slug}/${targetRepo.slug}.git`;
if (existsSync(targetRepoPath) === false) {
throw new Error(
`Could not find a valid target git repository at: ${targetRepoPath}`
);
}
const sourceRemoteName = `remotes/${sourceParentOrg.slug}/${sourceRepo.slug}`;
let isTargetRemoteSetup = false;
try {
const gitAddSourceRemoteInTargetRepoProcess = spawn(
"git",
["remote", "add", sourceRemoteName, sourceRepoPath],
{
cwd: targetRepoPath,
env: {
LANG: "C",
},
}
);
const gitAddSourceRemoteInTargetRepoResult = await new Promise<string>(
(resolve, reject) => {
let buffer = [] as string[];
gitAddSourceRemoteInTargetRepoProcess.stdout.on("data", (data) =>
buffer.push(data)
);
gitAddSourceRemoteInTargetRepoProcess.stderr.on("data", (data) => {
reject(new Error(Buffer.from(data).toString("utf-8")));
});
gitAddSourceRemoteInTargetRepoProcess.stdout.on("close", () => {
resolve(buffer.join(""));
});
}
);
isTargetRemoteSetup = gitAddSourceRemoteInTargetRepoResult != null;
} catch (err) {
const error = err as Error;
if (error.message.includes("already exists")) {
isTargetRemoteSetup = true;
} else {
isTargetRemoteSetup = false;
}
console.warn(
`Could not add remote "${sourceRemoteName}" pointing to "${sourceRepoPath}" in repository at: "${targetRepoPath}".\n\nError: ${error.message}`
);
}
if (isTargetRemoteSetup === false) {
return [];
}
try {
const gitFetchSourceBranchInTargetRepoProcess = spawn(
"git",
["fetch", sourceRemoteName],
{
cwd: targetRepoPath,
env: {
LANG: "C",
},
}
);
const gitFetchSourceBranchInTargetRepoResult = await new Promise<string>(
(resolve, reject) => {
let buffer = [] as string[];
gitFetchSourceBranchInTargetRepoProcess.stdout.on("data", (data) =>
buffer.push(data)
);
gitFetchSourceBranchInTargetRepoProcess.stderr.on("data", (data) => {
reject(new Error(Buffer.from(data).toString("utf-8")));
});
gitFetchSourceBranchInTargetRepoProcess.stdout.on("close", () => {
resolve(buffer.join(""));
});
}
);
isTargetRemoteSetup = gitFetchSourceBranchInTargetRepoResult != null;
} catch (err) {
isTargetRemoteSetup = false;
const error = err as Error;
console.warn(
`Could not fetch from remote "${sourceRemoteName}" in repository at: "${targetRepoPath}".\n\nError: ${error.message}`
);
}
if (isTargetRemoteSetup === false) {
return [];
}
try {
const gitDiffRefsProcess = spawn(
"git",
[
"diff",
`${targetDestBranch}..${sourceRemoteName}/${sourceFromBranch}`,
],
{
cwd: targetRepoPath,
env: {
LANG: "C",
},
}
);
const gitDiffRefsResult = await new Promise<string>((resolve, reject) => {
let buffer = [] as string[];
gitDiffRefsProcess.stdout.on("data", (data) => buffer.push(data));
gitDiffRefsProcess.stderr.on("data", (data) => {
reject(new Error(Buffer.from(data).toString("utf-8")));
});
gitDiffRefsProcess.stdout.on("close", () => {
resolve(buffer.join(""));
});
});
return parseDiff(gitDiffRefsResult, { findRenames: true });
} catch (err) {
const error = err as Error;
console.warn(
`Could not get diff between local branch "${targetDestBranch}" and remote branch "${sourceRemoteName}/${sourceFromBranch}" in repository at: "${targetRepoPath}".\n\nError: ${error.message}`
);
return [];
}
};
};
export default makeGetRepositoryRemoteRefDiff;
.ts
TypeScript
(application/typescript)
import { existsSync } from "node:fs";
import { spawn } from "node:child_process";
import { ServiceMethodFactory } from "@ethicdevs/react-monolith";
import parseDiff from "diffparser";
import type { Repository } from "@prisma/client";
import type { RepositoryFileDiff } from "../../types";
import { Env } from "../../env";
import type { RepositoryServiceDeps } from "./types";
import { default as makeGetRepositoryBranches } from "./getRepositoryBranches";
const makeGetRepositoryRemoteRefDiff: ServiceMethodFactory<
RepositoryServiceDeps,
[Repository, string, Repository, string],
Promise<RepositoryFileDiff[]>
> = ({ request }) => {
const getRepositoryBranches = makeGetRepositoryBranches({ request });
return async (sourceRepo, sourceFromBranch, targetRepo, targetDestBranch) => {
const sourceParentOrg = await request.prisma.organization.findUnique({
where: {
id: sourceRepo.organizationId,
},
});
if (sourceParentOrg == null) {
throw new Error(
`Could not find the parent organization for project "${sourceRepo.id}".`
);
}
const sourceBranches = await getRepositoryBranches(sourceRepo, true);
if (
sourceBranches.length <= 0 ||
sourceBranches.includes(sourceFromBranch) === false
) {
throw new Error(
`No such branch ${sourceFromBranch} found in repository "${sourceRepo.id}"`
);
}
const sourceRepoPath = `${Env.GIT_REPOSITORIES_ROOT}/${sourceParentOrg.slug}/${sourceRepo.slug}.git`;
if (existsSync(sourceRepoPath) === false) {
throw new Error(
`Could not find a valid source git repository at: ${sourceRepoPath}`
);
}
const targetParentOrg = await request.prisma.organization.findUnique({
where: {
id: targetRepo.organizationId,
},
});
if (targetParentOrg == null) {
throw new Error(
`Could not find the parent organization for project "${targetRepo.slug}".`
);
}
const targetBranches = await getRepositoryBranches(targetRepo, true);
if (
targetBranches.length <= 0 ||
targetBranches.includes(targetDestBranch) === false
) {
throw new Error(
`No such branch ${targetDestBranch} found in repository "${targetRepo.id}"`
);
}
const targetRepoPath = `${Env.GIT_REPOSITORIES_ROOT}/${targetParentOrg.slug}/${targetRepo.slug}.git`;
if (existsSync(targetRepoPath) === false) {
throw new Error(
`Could not find a valid target git repository at: ${targetRepoPath}`
);
}
const sourceRemoteName = `remotes/${sourceParentOrg.slug}/${sourceRepo.slug}`;
let isTargetRemoteSetup = false;
try {
const gitAddSourceRemoteInTargetRepoProcess = spawn(
"git",
["remote", "add", sourceRemoteName, sourceRepoPath],
{
cwd: targetRepoPath,
env: {
LANG: "C",
},
}
);
const gitAddSourceRemoteInTargetRepoResult = await new Promise<string>(
(resolve, reject) => {
let buffer = [] as string[];
gitAddSourceRemoteInTargetRepoProcess.stdout.on("data", (data) =>
buffer.push(data)
);
gitAddSourceRemoteInTargetRepoProcess.stderr.on("data", (data) => {
reject(new Error(Buffer.from(data).toString("utf-8")));
});
gitAddSourceRemoteInTargetRepoProcess.stdout.on("close", () => {
resolve(buffer.join(""));
});
}
);
isTargetRemoteSetup = gitAddSourceRemoteInTargetRepoResult != null;
} catch (err) {
const error = err as Error;
if (error.message.includes("already exists")) {
isTargetRemoteSetup = true;
} else {
isTargetRemoteSetup = false;
}
console.warn(
`Could not add remote "${sourceRemoteName}" pointing to "${sourceRepoPath}" in repository at: "${targetRepoPath}".\n\nError: ${error.message}`
);
}
if (isTargetRemoteSetup === false) {
return [];
}
try {
const gitFetchSourceBranchInTargetRepoProcess = spawn(
"git",
["fetch", sourceRemoteName],
{
cwd: targetRepoPath,
env: {
LANG: "C",
},
}
);
const gitFetchSourceBranchInTargetRepoResult = await new Promise<string>(
(resolve, reject) => {
let buffer = [] as string[];
gitFetchSourceBranchInTargetRepoProcess.stdout.on("data", (data) =>
buffer.push(data)
);
gitFetchSourceBranchInTargetRepoProcess.stderr.on("data", (data) => {
reject(new Error(Buffer.from(data).toString("utf-8")));
});
gitFetchSourceBranchInTargetRepoProcess.stdout.on("close", () => {
resolve(buffer.join(""));
});
}
);
isTargetRemoteSetup = gitFetchSourceBranchInTargetRepoResult != null;
} catch (err) {
isTargetRemoteSetup = false;
const error = err as Error;
console.warn(
`Could not fetch from remote "${sourceRemoteName}" in repository at: "${targetRepoPath}".\n\nError: ${error.message}`
);
}
if (isTargetRemoteSetup === false) {
return [];
}
try {
const gitDiffRefsProcess = spawn(
"git",
[
"diff",
`${targetDestBranch}..${sourceRemoteName}/${sourceFromBranch}`,
],
{
cwd: targetRepoPath,
env: {
LANG: "C",
},
}
);
const gitDiffRefsResult = await new Promise<string>((resolve, reject) => {
let buffer = [] as string[];
gitDiffRefsProcess.stdout.on("data", (data) => buffer.push(data));
gitDiffRefsProcess.stderr.on("data", (data) => {
reject(new Error(Buffer.from(data).toString("utf-8")));
});
gitDiffRefsProcess.stdout.on("close", () => {
resolve(buffer.join(""));
});
});
return parseDiff(gitDiffRefsResult, { findRenames: true });
} catch (err) {
const error = err as Error;
console.warn(
`Could not get diff between local branch "${targetDestBranch}" and remote branch "${sourceRemoteName}/${sourceFromBranch}" in repository at: "${targetRepoPath}".\n\nError: ${error.message}`
);
return [];
}
};
};
export default makeGetRepositoryRemoteRefDiff;