.ts
TypeScript
(application/typescript)
// std
import { existsSync } from "node:fs";
import { copyFile, mkdir } from "node:fs/promises";
import { join, resolve } from "node:path";
// 1st-party
import type { ServiceMethodFactory } from "@ethicdevs/react-monolith";
// generated via script[generate:prisma]
import type { Repository } from "@prisma/client";
// service
import type { ForkRepositoryDTO, RepositoryServiceDeps } from "./types";

const makeForkRepository: ServiceMethodFactory<
  RepositoryServiceDeps,
  [ForkRepositoryDTO],
  Promise<Repository>
> = ({ request }) => {
  return async ({ source, target }) => {
    if (
      source == null ||
      source.parentOrg == null ||
      source.repository == null
    ) {
      throw new Error(
        "Cannot fork repository: invalid source object (either it is `null`, or its `parentOrg` and/or `repository` properties are `null`)."
      );
    }

    if (
      target == null ||
      target.parentOrg == null ||
      target.repoSlug == null ||
      target.repoData == null
    ) {
      throw new Error(
        "Cannot fork repository: invalid target object (either it is `null`, or its `parentOrg`, `repoSlug`, and/or `repoData` properties are `null`)."
      );
    }

    let existingRepoWithSameSlugInSameTargetOrg =
      await request.prisma.repository.findFirst({
        where: {
          slug: target.repoSlug,
          organization: {
            id: target.parentOrg.id,
          },
        },
      });

    if (existingRepoWithSameSlugInSameTargetOrg != null) {
      throw new Error(
        "Cannot fork repository: a repository with the same slug already exists in this organization."
      );
    }

    // no longer needed, free it right away.
    existingRepoWithSameSlugInSameTargetOrg = null;

    console.log(`[..] creating target fork repository in database...`);

    const newRepo = await request.prisma.repository.create({
      data: {
        ...target.repoData,
        organizationId: target.parentOrg.id,
        slug: target.repoSlug,
      },
    });

    console.log(
      `[ok] created target fork repository in database with id "${newRepo.id}" and slug "${target.parentOrg.slug}/${newRepo.slug}" from source repo with id "${source.repository.id}" and slug "${source.parentOrg.slug}/${source.repository.slug}" !`
    );

    if (existsSync(target.parentOrgRepositoriesDir.toString()) === false) {
      console.log(`[..] creating organization directory...`);
      await mkdir(target.parentOrgRepositoriesDir.toString(), {
        recursive: true,
      });
      console.log(
        `[ok] created organization directory in:`,
        target.parentOrgRepositoriesDir.toString()
      );
    }

    const sourceRepositoryPathResolved = resolve(
      join(
        source.parentOrgRepositoriesDir.toString(),
        `${source.repository.slug}.git`
      )
    );

    if (existsSync(sourceRepositoryPathResolved) === false) {
      throw new Error(
        "Cannot fork repository: no .git/ folder exists for source repository in source organization."
      );
    }

    const targetRepositoryPathResolved = resolve(
      join(target.parentOrgRepositoriesDir.toString(), `${target.repoSlug}.git`)
    );

    if (existsSync(targetRepositoryPathResolved) === true) {
      throw new Error(
        "Cannot fork repository: a .git/ folder with the same name already exists in target organization."
      );
    }

    console.log(
      `[..] forking repository folder from:`,
      sourceRepositoryPathResolved,
      `to:`,
      targetRepositoryPathResolved
    );

    await copyFile(sourceRepositoryPathResolved, targetRepositoryPathResolved);

    console.log(
      `[ok] forked repository folder from:`,
      sourceRepositoryPathResolved,
      `to:`,
      targetRepositoryPathResolved,
      "->",
      newRepo
    );

    return newRepo;
  };
};

export default makeForkRepository;