feat(repository): add service methods "getRepositoryBranches" and "getRepositoryTags"
+ 132
- 0
new file
app/services/repository/getRepositoryBranches.ts
@@ -0,0 +1,63 @@
+// std
+import { existsSync } from "node:fs";
+import { spawn } from "node:child_process";
+// 1st-party
+import { ServiceMethodFactory } from "@ethicdevs/react-monolith";
+// generated via script[generate:prisma]
+import type { Repository } from "@prisma/client";
+// app
+import { Env } from "../../env";
+// service
+import { RepositoryServiceDeps } from "./types";
+
+const makeGetRepositoryBranches: ServiceMethodFactory<
+  RepositoryServiceDeps,
+  [Repository],
+  Promise<string[]>
+> = ({ request }) => {
+  return async (repo) => {
+    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}".`
+      );
+    }
+
+    try {
+      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}`
+        );
+      }
+
+      const gitBranchProcess = spawn("git", ["branch", "-a"], {
+        cwd: repoPath,
+      });
+
+      const gitBranchResult = await new Promise<string>((resolve, reject) => {
+        let buffer = [] as string[];
+        gitBranchProcess.stdout.on("data", (data) => buffer.push(data));
+        gitBranchProcess.stderr.on("data", (data) => {
+          reject(new Error(Buffer.from(data).toString("utf-8")));
+        });
+        gitBranchProcess.stdout.on("close", () => {
+          resolve(buffer.join(""));
+        });
+      });
+
+      return gitBranchResult
+        .split("\n")
+        .map((branch) => branch.trim().replace(/^\* /i, ""));
+    } catch (_) {
+      return [];
+    }
+  };
+};
+
+export default makeGetRepositoryBranches;

new file
app/services/repository/getRepositoryTags.ts
@@ -0,0 +1,63 @@
+// std
+import { existsSync } from "node:fs";
+import { spawn } from "node:child_process";
+// 1st-party
+import { ServiceMethodFactory } from "@ethicdevs/react-monolith";
+// generated via script[generate:prisma]
+import type { Repository } from "@prisma/client";
+// app
+import { Env } from "../../env";
+// service
+import { RepositoryServiceDeps } from "./types";
+
+const makeGetRepositoryTags: ServiceMethodFactory<
+  RepositoryServiceDeps,
+  [Repository],
+  Promise<string[]>
+> = ({ request }) => {
+  return async (repo) => {
+    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}".`
+      );
+    }
+
+    try {
+      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}`
+        );
+      }
+
+      const gitTagProcess = spawn("git", ["tag", "--sort=taggerdate"], {
+        cwd: repoPath,
+      });
+
+      const gitTagResult = await new Promise<string>((resolve, reject) => {
+        let buffer = [] as string[];
+        gitTagProcess.stdout.on("data", (data) => buffer.push(data));
+        gitTagProcess.stderr.on("data", (data) => {
+          reject(new Error(Buffer.from(data).toString("utf-8")));
+        });
+        gitTagProcess.stdout.on("close", () => {
+          resolve(buffer.join(""));
+        });
+      });
+
+      return gitTagResult
+        .split("\n")
+        .map((branch) => branch.trim().replace(/^\* /i, ""));
+    } catch (_) {
+      return [];
+    }
+  };
+};
+
+export default makeGetRepositoryTags;

app/services/repository/index.ts
@@ -5,6 +5,7 @@ import type { RepositoryServiceAPI, RepositoryServiceDeps } from "./types";
 // service methods
 import { default as makeCreateRepository } from "./createRepository";
 import { default as makeGetRepository } from "./getRepository";
+import { default as makeGetRepositoryBranches } from "./getRepositoryBranches";
 import { default as makeGetRepositoryCommitLog } from "./getRepositoryCommitLog";
 import { default as makeGetRepositoryExploreCollection } from "./getRepositoryExploreCollection";
 import { default as makeGetRepositoryFileContent } from "./getRepositoryFileContent";

...
@@ -14,6 +15,7 @@ import { default as makeGetRepositoryHead } from "./getRepositoryHead";
 import { default as makeGetRepositoryHTTPCloneUrl } from "./getRepositoryHTTPCloneUrl";
 import { default as makeGetRepositorySSHCloneUrl } from "./getRepositorySSHCloneUrl";
 import { default as makeGetRepositoryRefDiff } from "./getRepositoryRefDiff";
+import { default as makeGetRepositoryTags } from "./getRepositoryTags";
 import { default as makeIsFileInRepositoryPath } from "./isFileInRepositoryPath";
 
 export const makeRepositoryService = makeService<

...
@@ -22,6 +24,7 @@ export const makeRepositoryService = makeService<
 >({
   createRepository: makeCreateRepository,
   getRepository: makeGetRepository,
+  getRepositoryBranches: makeGetRepositoryBranches,
   getRepositoryCommitLog: makeGetRepositoryCommitLog,
   getRepositoryExploreCollection: makeGetRepositoryExploreCollection,
   getRepositoryFileContent: makeGetRepositoryFileContent,

...
@@ -31,5 +34,6 @@ export const makeRepositoryService = makeService<
   getRepositoryHTTPCloneUrl: makeGetRepositoryHTTPCloneUrl,
   getRepositorySSHCloneUrl: makeGetRepositorySSHCloneUrl,
   getRepositoryRefDiff: makeGetRepositoryRefDiff,
+  getRepositoryTags: makeGetRepositoryTags,
   isFileInRepositoryPath: makeIsFileInRepositoryPath,
 });

app/services/repository/types.ts
@@ -33,6 +33,7 @@ export interface CreateRepositoryDTO {
 export interface RepositoryServiceAPI extends ServiceApiContract {
   createRepository(dto: CreateRepositoryDTO): Promise<Repository>;
   getRepository(orgSlug: string, repoSlug: string): Promise<Repository | null>;
+  getRepositoryBranches(repository: Repository): Promise<string[]>;
   getRepositoryCommitLog(
     repository: Repository,
     path?: string,

...
@@ -68,6 +69,7 @@ export interface RepositoryServiceAPI extends ServiceApiContract {
     refA: string,
     refB?: string
   ): Promise<RepositoryFileDiff[]>;
+  getRepositoryTags(repository: Repository): Promise<string[]>;
   isFileInRepositoryPath(
     repository: Repository,
     path: string,