GitFOSS
.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);

    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);

    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 {
      // - gitAddSourceRemoteInTargetRepoProcess
      spawn("git", ["remote", "add", sourceRemoteName, sourceRepoPath], {
        cwd: targetRepoPath,
      });
      isTargetRemoteSetup = true;
    } 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 gitDiffRefsProcess = spawn(
        "git",
        [
          "diff",
          `${sourceRemoteName}/${sourceFromBranch}..${targetDestBranch}`,
        ],
        {
          cwd: targetRepoPath,
        }
      );

      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;