.ts
TypeScript
(application/typescript)
// std
import { existsSync } from "node:fs";
import { spawn } from "node:child_process";
// 1st-party
import { ServiceMethodFactory } from "@ethicdevs/react-monolith";
// 3rd-party
import parseDiff from "diffparser";
// generated via script[generate:prisma]
import type { Repository } from "@prisma/client";
// app
import type { RepositoryFileDiff } from "../../types";
import { Env } from "../../env";
// service
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) => {
    // - source
    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}`
      );
    }

    // - target
    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;

GitFOSS • v0.2.0 (#421408f) • MIT License