GitFOSS
feat(server): expose request.gitStamp and display it in the footer so running Git hash is known
+ 35
- 7
@@ -7,6 +7,7 @@ package-lock.json
 *.log
 
 # Ignore built public files
+.gitstamp
 public/.islands/
 public/instant-router.js
 public/instant-router.js.map

@@ -39,13 +39,12 @@ RUN yarn test              # Test code to ensure it is regression-free (jest)
 RUN yarn clean             # Cleanup working dir
 RUN yarn build:ts          # Transpile TypeScript to JavaScript (node)
 RUN yarn bundle:islands    # Bundle Islands (react-monolith) to ESM/CJS/UMD
+RUN yarn gitstamp          # Make gitstamp file with built commit hash
 
 COPY ./public              /usr/src/app/public
+COPY ./.gitstamp           /usr/src/app/.gitstamp
 COPY ./app.manifest.json   /usr/src/app/app.manifest.json
 
-# Issue a git stamp file containing built commit hash
-RUN echo "$(git rev-parse HEAD)" >> .gitstamp
-
 FROM node:slim as base
 
 ENV NODE_ENV=production

...
@@ -72,8 +71,6 @@ COPY --from=builder /usr/src/app/ReadMe.md      /app/ReadMe.md
 COPY --from=builder /usr/src/app/LICENSE        /app/LICENSE
 COPY --from=builder /usr/src/app/yarn.lock      /app/yarn.lock
 
-RUN echo "Running from Git commit: $(cat .gitstamp)"
-
 EXPOSE ${PORT}
 
 CMD ["node", "app/server.js"]

app/components/Layout.tsx
@@ -21,7 +21,7 @@ function removeCommentsAndSpacing(str = "") {
 }
 
 export const Layout: FC<LayoutProps & WithThemeSchemeProp> = (commonProps) => {
-  const { appVersion, children, themeScheme } = commonProps;
+  const { appVersion, children, gitStamp, themeScheme } = commonProps;
 
   const sharedProps = {
     themeScheme,

...
@@ -84,7 +84,7 @@ export const Layout: FC<LayoutProps & WithThemeSchemeProp> = (commonProps) => {
         <StyledFooterWrapper>
           <p>
             <a href={"https://gitfoss.io/ethicdevs/gitfoss"}>
-              GitFOSS - v{appVersion} - MIT License
+              GitFOSS - v{appVersion} (#{gitStamp.slice(0, 8)}) - MIT License
             </a>
           </p>
         </StyledFooterWrapper>

@@ -33,6 +33,7 @@ import { codeAnalysisPlugin, cryptoPlugin, prismaPlugin } from "./plugins";
 import { makeGitServerService } from "./services/gitServer";
 import {
   getEnv,
+  getGitStamp,
   localAppDomainPreHandler,
   makeRequestHandler,
 } from "./utils/server";

...
@@ -320,6 +321,13 @@ async function main(): Promise<AppServer> {
     },
   });
 
+  const gitStamp = await getGitStamp();
+  console.log(`- Running from Git ref: ${gitStamp}`);
+
+  server.decorateRequest("gitStamp", {
+    getter: () => gitStamp,
+  });
+
   await startAppServer(server);
 
   return server;

@@ -31,6 +31,7 @@ export interface CommonViewProps {
   currentUserId: string | null;
   currentUserRole: GlobalRole | null;
   currentUserUsername: string | null;
+  gitStamp: string;
   flashMessage: string | null;
   themeScheme: AppThemeScheme;
   title?: string;

new file
app/utils/server/getGitStamp.ts
@@ -0,0 +1,16 @@
+import { join, resolve } from "node:path";
+import { readFile } from "node:fs/promises";
+
+export async function getGitStamp() {
+  try {
+    const gitStampFileContents = await readFile(
+      resolve(join(__dirname, "..", "..", "..", ".gitstamp")),
+      {
+        encoding: "utf-8",
+      }
+    );
+    return gitStampFileContents.split("\n")[0];
+  } catch (err) {
+    return "HEAD";
+  }
+}

app/utils/server/index.ts
@@ -3,6 +3,7 @@
 export { authenticatedOrLogin } from "./authenticatedOrLogin";
 export { authenticatedOrRedirect } from "./authenticatedOrRedirect";
 export { getEnv } from "./getEnv";
+export { getGitStamp } from "./getGitStamp";
 export { guestOrRedirect } from "./guestOrRedirect";
 export { localAppDomainPreHandler } from "./localAppDomainPreHandler";
 export { makeRequestHandler } from "./makeRequestHandler";

app/utils/server/makeRequestHandler.ts
@@ -45,6 +45,7 @@ export const makeRequestHandler = {
             currentUserId: curr_user_uid,
             currentUserRole: curr_user_role,
             currentUserUsername: curr_user_username,
+            gitStamp: request.gitStamp,
             flashMessage: flash_message,
             title,
             themeScheme,

@@ -12,6 +12,7 @@
     "generate": "run-s generate:prisma",
     "generate:prisma": "prisma generate",
     "generate:prisma-data-proxy": "prisma generate --data-proxy",
+    "gitstamp": "git rev-parse HEAD > .gitstamp",
     "db:push": "prisma db push --preview-feature",
     "migrate:dev": "prisma migrate dev",
     "migrate:deploy": "prisma migrate deploy",

types/global/index.d.ts
@@ -32,6 +32,8 @@ declare module "fastify" {
     codeAnalysisService: CodeAnalysisServiceAPI;
     // from crypto plugin
     cryptoService: CryptoServiceAPI;
+    // from server file
+    gitStamp: string;
     // from react-monolith: request utility that maps a viewName to its routerPath
     namedViewsPathMap: Record<string, string>;
     // from react-monolith: request utility that maps a routerPath to its viewName