chore: git server plumbing
+ 195
- 17
app/components/DrawerPrimary.tsx
@@ -31,8 +31,8 @@ export const DrawerPrimary = ({
   themeScheme,
   orgSlug,
   repoSlug,
-  currentRef = Const.DEFAULT_HEAD_REF,
-  path = "/",
+  // currentRef = Const.DEFAULT_HEAD_REF,
+  // path = "/",
   counters = {
     pulls: 0,
     tests: 0,

...
@@ -62,11 +62,11 @@ export const DrawerPrimary = ({
     },
   );
 
-  const pathFiles = buildRouteLink(AppRoute.REPOSITORY_BROWSER, {
+  const pathFiles = buildRouteLink(AppRoute.REPOSITORY_DETAILS, {
     orgSlug: orgSlug,
     repoSlug: repoSlug,
-    currentRef: currentRef,
-    "*": path,
+    // currentRef: currentRef,
+    // "*": path,
   });
 
   const pathPulls = buildRouteLink(AppRoute.REPOSITORY_PULL_REQUESTS, {

@@ -293,6 +293,7 @@ async function main(): Promise<AppServer> {
           authorizationResolver: gitService.authorizationResolver,
           repositoryResolver: gitService.repositoryResolver,
           onPush: gitService.onPushEvent,
+          onFetch: gitService.onFetchEvent,
         });
       });
 

app/services/gitServer/authorizationResolver.ts
@@ -93,10 +93,16 @@ const makeAuthorizationResolver: ServiceMethodFactory<
           matchingPk.user.id === user.id &&
           matchingPk.user.username === username
         );
-      } else {
-        const hashedPassword = cryptoService.computeHash(password);
-        return hashedPassword === user.hashedPassword;
       }
+
+      const hashedPassword = cryptoService.computeHash(password);
+      const authed = hashedPassword === user.hashedPassword;
+
+      console.log("git.authorizationResolver:", authed);
+
+      console.log("git.authorizationResolver:", authed, username);
+
+      return authed;
     }
   };
 };

app/services/gitServer/index.ts
@@ -6,6 +6,7 @@ import { GitServerServiceAPI, GitServerServiceDeps } from "./types";
 import { default as makeAuthorizationResolver } from "./authorizationResolver";
 import { default as makeRepositoryResolver } from "./repositoryResolver";
 import { default as makeOnPushEvent } from "./onPushEvent";
+import { default as makeOnFetchEvent } from "./onFetchEvent";
 
 export const makeGitServerService = makeService<
   GitServerServiceAPI,

...
@@ -14,4 +15,5 @@ export const makeGitServerService = makeService<
   authorizationResolver: makeAuthorizationResolver,
   repositoryResolver: makeRepositoryResolver,
   onPushEvent: makeOnPushEvent,
+  onFetchEvent: makeOnFetchEvent,
 });

new file
app/services/gitServer/onFetchEvent.ts
@@ -0,0 +1,74 @@
+// 1st-party
+import type { ServiceMethodFactory } from "@ethicdevs/react-monolith";
+import { GitServer } from "@ethicdevs/fastify-git-server";
+// app
+// import { Const } from "../../const";
+import { Env } from "../../env";
+import { GitServerServiceDeps } from "./types";
+import { getEnv } from "../../utils/server";
+
+const makeOnFetchEvent: ServiceMethodFactory<
+  GitServerServiceDeps,
+  [GitServer.Event],
+  void
+> = ({ request }) => {
+  const env = getEnv();
+  return ({ type, data, message }) => {
+    console.log("upload-pack", type, data, message);
+
+    if (data.packType === GitServer.PackType.UPLOAD) {
+      // client has done something like "git fetch/pull" it is receiving
+      // server is uploading to client
+
+      console.log("git.onFetchEvent:", data, message);
+
+      const [orgSlug, repoSlug] = data.repoSlug.split("/");
+      request.prisma.repository
+        .findFirst({
+          where: {
+            slug: repoSlug,
+            organization: {
+              slug: orgSlug,
+            },
+          },
+        })
+        .then(async (repo) => {
+          if (repo == null) return Promise.resolve(null);
+          const updatedRepo = await request.prisma.repository.update({
+            where: {
+              id: repo.id,
+            },
+            data: {
+              lastPushedAt: new Date(Date.now()),
+            },
+          });
+          return updatedRepo;
+        });
+
+      const repoUrlPrefix = `${Env.DEPLOYMENT_SCHEME}://${Env.DEPLOYMENT_DOMAIN}${Env.DEPLOYMENT_SCHEME !== "https" ? `:${Env.PORT}` : ""}`;
+      const repoUrlSuffix = `${orgSlug}/${repoSlug}`;
+      const repoUrlBase =
+        env === "production"
+          ? `${repoUrlPrefix}/${repoUrlSuffix}`
+          : `${repoUrlPrefix}:${Env.PORT}/${repoUrlSuffix}`;
+
+      // message.write(`Hey ${data.username}, welcome back.\n\n`);
+      // message.write(toAscii(`📖 See the details of your push at:\n`));
+
+      console.log("repoUrlBase:", repoUrlBase);
+      console.log("payload:", data.payload);
+
+      // if (data.payload.refType === "head") {
+      //   console.log("refType:", "branch");
+      // }
+      // if (data.payload.refType === "tag") {
+      //   console.log("refType:", "tag");
+      // }
+    }
+
+    // Finally, accept the payload from client
+    // message.accept();
+  };
+};
+
+export default makeOnFetchEvent;

app/services/gitServer/onPushEvent.ts
@@ -9,12 +9,14 @@ import { getEnv } from "../../utils/server";
 
 const toAscii = (str: string) => {
   // replace emoji with nothing
-  return str
-    .replace(/[\u{1F600}-\u{1F64F}]/gu, "")
-    .replace(
-      /[\p{Emoji_Presentation}\p{Extended_Pictographic}\p{Emoji}\uFE0F]/gu,
-      "",
-    );
+  return (
+    str
+      // .replace(/[\u{1F600}-\u{1F64F}]/gu, "")
+      .replace(
+        /[\p{Emoji_Presentation}\p{Extended_Pictographic}\p{Emoji}\uFE0F]/gu,
+        "",
+      )
+  );
 };
 
 const makeOnPushEvent: ServiceMethodFactory<

...
@@ -23,13 +25,32 @@ const makeOnPushEvent: ServiceMethodFactory<
   void
 > = ({ request }) => {
   const env = getEnv();
-  return ({ data, message }) => {
+  return ({ type, data, message }) => {
     if (data.packType === GitServer.PackType.RECEIVE) {
       // client has done something like "git push" it is uploading
       // server is receiving
       console.log("receive-pack");
       message.write("\n");
 
+      console.log("git.onPushEvent:", type, data, message);
+      console.log("git.onPushEvent: data.payload.metas:", data.payload?.metas);
+
+      // git push:
+      // on data.requestType === 'git-receive-pack'
+      //  + data.packType === 'receive-pack'
+      // - data.payload.commitId  # id of incoming commit
+      // - data.payload.refType   # "head" or "tag"
+      // - data.payload.refName   # the branch or tag name
+      // - data.repoDiskPath      # the path to the repository on disk
+      // - data.request.id        # the request id
+
+      // git push --tags:
+      // on data.requestType === 'git-receive-pack'
+      //  + data.packType === 'receive-pack'
+      // - data.payload = null
+      // - data.repoDiskPath      # the path to the repository on disk
+      // - data.request.id        # the request id
+
       const [orgSlug, repoSlug] = data.repoSlug.split("/");
       request.prisma.repository
         .findFirst({

...
@@ -101,6 +122,8 @@ const makeOnPushEvent: ServiceMethodFactory<
       console.log("upload-pack");
       // ⬇️ disabled because it seem to cause issues on git pull. ⬇️
       // message.write(toAscii(`🖖 Welcome at GitFOSS ${data.username}!\n`));
+
+      console.log("upload-pack:", data, message);
     }
 
     // Finally, accept the payload from client

app/services/gitServer/repositoryResolver.ts
@@ -49,6 +49,8 @@ const makeRepositoryResolver: ServiceMethodFactory<
       join(Env.GIT_REPOSITORIES_ROOT, org.slug, repo.slug),
     );
 
+    console.log("gitRepositoryDir:", gitRepositoryDir);
+
     if (
       repo.visibility === ResourceVisibility.PUBLIC ||
       repo.visibility === ResourceVisibility.UNLISTED

app/services/gitServer/types.ts
@@ -9,12 +9,13 @@ import type { CryptoServiceAPI } from "../crypto/types";
 export interface GitServerServiceAPI extends ServiceApiContract {
   authorizationResolver(
     repoSlug: string,
-    credentials: GitServer.AuthCredentials
+    credentials: GitServer.AuthCredentials,
   ): PromiseLike<boolean>;
   repositoryResolver(
-    repoSlug: string
+    repoSlug: string,
   ): PromiseLike<GitServer.RepositoryResolverResult>;
   onPushEvent(event: GitServer.Event): void;
+  onFetchEvent: (event: GitServer.Event) => void;
 }
 
 export interface GitServerServiceDeps {

new file
patches/@ethicdevs+fastify-git-server+1.6.1.patch
@@ -0,0 +1,69 @@
+diff --git a/node_modules/@ethicdevs/fastify-git-server/dist/helpers/sendInfoRefs.js b/node_modules/@ethicdevs/fastify-git-server/dist/helpers/sendInfoRefs.js
+index 6a9b61e..1bb563e 100644
+--- a/node_modules/@ethicdevs/fastify-git-server/dist/helpers/sendInfoRefs.js
++++ b/node_modules/@ethicdevs/fastify-git-server/dist/helpers/sendInfoRefs.js
+@@ -6,16 +6,31 @@ var constants_1 = require("../constants");
+ var getGitPackMagicCode_1 = require("./getGitPackMagicCode");
+ var safeServiceToPackType_1 = require("./safeServiceToPackType");
+ var spawnGit_1 = require("./spawnGit");
+-function sendInfoRefs(opts, packType, cwd, gitStream) {
+-    return new Promise(function (resolve) {
+-        var safePackType = (0, safeServiceToPackType_1.safeServiceToPackType)(packType);
+-        var process = (0, spawnGit_1.spawnGit)(opts, [safePackType, constants_1.GIT_STATELESS_RPC_FLAG, constants_1.GIT_ADVERTISE_REFS_FLAG], cwd);
+-        gitStream.write((0, getGitPackMagicCode_1.getGitPackMagicCode)(safePackType) +
+-            " service=git-" +
+-            safePackType +
+-            "\n0000");
+-        process.stdout.on("data", function (chunk) { return gitStream.write(chunk); });
+-        process.stdout.on("close", function () { return resolve(gitStream.end()); });
++function sendInfoRefs(_a) {
++  var cwd = _a.cwd, gitStream = _a.gitStream, opts = _a.opts, packType = _a.packType, repoSlug = _a.repoSlug, request = _a.request, requestMethod = _a.requestMethod, requestType = _a.requestType, username = _a.username;
++  return new Promise(function (resolve) {
++    opts?.onFetch({
++      type: "fetch",
++      message: null,
++      data: {
++        packType,
++        payload: null,
++        repoDiskPath: cwd,
++        repoSlug,
++        request,
++        requestMethod,
++        requestType,
++        username,
++      },
+     });
++    var safePackType = (0, safeServiceToPackType_1.safeServiceToPackType)(packType);
++    var process = (0, spawnGit_1.spawnGit)(opts, [safePackType, constants_1.GIT_STATELESS_RPC_FLAG, constants_1.GIT_ADVERTISE_REFS_FLAG], cwd);
++    gitStream.write((0, getGitPackMagicCode_1.getGitPackMagicCode)(safePackType) +
++        " service=git-" +
++        safePackType +
++        "\n0000");
++    process.stdout.on("data", function (chunk) { return gitStream.write(chunk); });
++    process.stdout.on("close", function () { return resolve(gitStream.end()); });
++  });
+ }
+ exports.sendInfoRefs = sendInfoRefs;
+diff --git a/node_modules/@ethicdevs/fastify-git-server/dist/pluginFactory.js b/node_modules/@ethicdevs/fastify-git-server/dist/pluginFactory.js
+index 1d677a6..fe3e421 100644
+--- a/node_modules/@ethicdevs/fastify-git-server/dist/pluginFactory.js
++++ b/node_modules/@ethicdevs/fastify-git-server/dist/pluginFactory.js
+@@ -109,7 +109,17 @@ var gitServerPluginAsync = function (server, opts) { return tslib_1.__awaiter(vo
+                             reply.raw.writeHead(200, "OK", {
+                                 "content-type": "application/x-git-".concat(packType, "-advertisement"),
+                             });
+-                            return [4 /*yield*/, (0, sendInfoRefs_1.sendInfoRefs)(opts, packType, repoResult.gitRepositoryDir, gitStream)];
++                            return [4 /*yield*/, (0, sendInfoRefs_1.sendInfoRefs)({
++                              cwd: repoResult.gitRepositoryDir,
++                              gitStream: gitStream,
++                              opts: opts,
++                              packType: packType,
++                              repoSlug: repoSlug,
++                              request: request,
++                              requestMethod: requestMethod,
++                              requestType: requestType,
++                              username: (authCredentials === null || authCredentials === void 0 ? void 0 : authCredentials.username) || null,
++                            })];
+                         case 4:
+                             _c.sent();
+                             return [3 /*break*/, 11];