Add git information to create-react-app
Asked Answered
H

9

57

In development, I want to be able to see the build information (git commit hash, author, last commit message, etc) from the web. I have tried:

  • use child_process to execute a git command line, and read the result (Does not work because browser environment)
  • generate a buildInfo.txt file during npm build and read from the file (Does not work because fs is also unavailable in browser environment)
  • use external libraries such as "git-rev"

The only thing left to do seems to be doing npm run eject and applying https://www.npmjs.com/package/git-revision-webpack-plugin , but I really don't want to eject out of create-react-app. Anyone got any ideas?

Hardpressed answered 22/1, 2018 at 23:30 Comment(0)
H
9

So, turns out there is no way to achieve this without ejecting, so the workaround I used is:

1) in package.json, define a script "git-info": "git log -1 --oneline > src/static/gitInfo.txt"

2) add npm run git-info for both start and build

3) In the config js file (or whenever you need the git info), i have

const data = require('static/gitInfo.txt')
fetch(data).then(result => {
    return result.text()
})
Hardpressed answered 24/2, 2018 at 0:34 Comment(0)
F
99

On a slight tangent (no need to eject and works in develop), this may be of help to other folk looking to add their current git commit SHA into their index.html as a meta-tag by adding:

REACT_APP_GIT_SHA=`git rev-parse --short HEAD`

to the build script in the package.json and then adding (note it MUST start with REACT_APP... or it will be ignored):

<meta name="ui-version" content="%REACT_APP_GIT_SHA%">

into the index.html in the public folder.

Within react components, do it like this:

<Component>{process.env.REACT_APP_GIT_SHA}</Component>
Freezedry answered 29/8, 2018 at 14:30 Comment(10)
I like it, even though it does not work on localhost when developing, only after building a production version.Expressage
@BugsBunny Should work the same way. Just add the assignment to start script as well. "start": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` react-scripts start"Watchdog
@Freezedry how did you know to use backticks to get substitution working in a package.json script?? I just blew a couple of hours pulling my hair out trying to get this to work before stumbling on your solution. It worked perfectly, btw!Bear
This is not a cross-platform solution. Will not work on Windows with either Powershell or cmd.Glossitis
@AbhyudayaSharma cross-env could very easily replace the mentioned scriptGraiae
cross-env will not work for this without having to change your npm config; see here: github.com/kentcdodds/cross-env/issues/…Contemplation
This syntax does not work with yarn 2, however the $() syntax does - see unix.stackexchange.com/questions/48392/understanding-backtickProfusive
"REACT_APP_GIT_SHA=git rev-parse --short HEAD react-scripts start". How can I run this command in windows cmd?Sleave
@RohitVyas not really sure what you mean exactly but if you type: git rev-parse --short HEAD into the CLI you will get that git commit SHA like "17c0b12". for "REACT_APP_GIT_SHA=git rev-parse --short HEAD I run it by assigning it to a npm script in the package.json file and for example I call it build So then in the CLI if I run npm build it will execute this script. Hope this is of some help, thanks.Freezedry
I asked because this command is working in mac system but not working in windows cmd.Sleave
G
15

I created another option inspired by Yifei Xu's response that utilizes es6 modules with create-react-app. This option creates a javascript file and imports it as a constant inside of the build files. While having it as a text file makes it easy to update, this option ensures it is a js file packaged into the javascript bundle. The name of this file is _git_commit.js

package.json scripts:

"git-info": "echo export default \"{\\\"logMessage\\\": \\\"$(git log -1 --oneline)\\\"}\"  > src/_git_commit.js",
"precommit": "lint-staged",
"start": "yarn git-info; react-scripts start",
"build": "yarn git-info; react-scripts build",

A sample component that consumes this commit message:

import React from 'react';

/**
 * This is the commit message of the last commit before building or running this project
 * @see ./package.json git-info for how to generate this commit
 */
import GitCommit from './_git_commit';

const VersionComponent = () => (
  <div>
    <h1>Git Log: {GitCommit.logMessage}</h1>
  </div>
);

export default VersionComponent;

Note that this will automatically put your commit message in the javascript bundle, so do ensure no secure information is ever entered into the commit message. I also add the created _git_commit.js to .gitignore so it's not checked in (and creates a crazy git commit loop).

Greathearted answered 18/4, 2018 at 17:53 Comment(0)
G
11

It was impossible to be able to do this without ejecting until Create React App 2.0 (Release Notes) which brought with it automatic configuration of Babel Plugin Macros which run at compile time. To make the job simpler for everyone, I wrote one of those macros and published an NPM package that you can import to get git information into your React pages: https://www.npmjs.com/package/react-git-info

With it, you can do it like this:

import GitInfo from 'react-git-info/macro';

const gitInfo = GitInfo();

...

render() {
  return (
    <p>{gitInfo.commit.hash}</p>
  );
}

The project README has some more information. You can also see a live demo of the package working here.

Glossitis answered 18/1, 2020 at 10:21 Comment(1)
While this does add another package it is a super-clean & useful way to achieve the goal.Greco
H
9

So, turns out there is no way to achieve this without ejecting, so the workaround I used is:

1) in package.json, define a script "git-info": "git log -1 --oneline > src/static/gitInfo.txt"

2) add npm run git-info for both start and build

3) In the config js file (or whenever you need the git info), i have

const data = require('static/gitInfo.txt')
fetch(data).then(result => {
    return result.text()
})
Hardpressed answered 24/2, 2018 at 0:34 Comment(0)
E
9

My approach is slightly different from @uidevthing's answer. I don't want to pollute package.json file with environment variable settings.

You simply have to run another script that save those environment variables into .env file at the project root. That's it.

In the example below, I'll use typescript but it should be trivial to convert to javascript anyway.

package.json

If you use javascript it's node scripts/start.js

  ...
  "start": "ts-node scripts/start.ts && react-scripts start",

scripts/start.ts

Create a new script file scripts/start.ts

const childProcess = require("child_process");
const fs = require("fs");

function writeToEnv(key: string = "", value: string = "") {
  const empty = key === "" && value === "";

  if (empty) {
    fs.writeFile(".env", "", () => {});
  } else {
    fs.appendFile(".env", `${key}='${value.trim()}'\n`, (err) => {
      if (err) console.log(err);
    });
  }
}

// reset .env file
writeToEnv();

childProcess.exec("git rev-parse --abbrev-ref HEAD", (err, stdout) => {
  writeToEnv("REACT_APP_GIT_BRANCH", stdout);
});
childProcess.exec("git rev-parse --short HEAD", (err, stdout) => {
  writeToEnv("REACT_APP_GIT_SHA", stdout);
});

// trick typescript to think it's a module
// https://mcmap.net/q/108547/-why-is-isolatedmodules-error-fixed-by-any-import
export {};

The code above will setup environment variables and save them to .env file at the root folder. They must start with REACT_APP_. React script then automatically reads .env at build time and then defines them in process.env.

App.tsx

...
console.log('REACT_APP_GIT_BRANCH', process.env.REACT_APP_GIT_BRANCH)
console.log('REACT_APP_GIT_SHA', process.env.REACT_APP_GIT_SHA)

Result

REACT_APP_GIT_BRANCH master
REACT_APP_GIT_SHA 042bbc6

More references:

Erhard answered 13/7, 2020 at 15:43 Comment(2)
Worked like a charm 👍🏻🎉Melodic
works well for react nativeCongelation
W
2

If your package.json scripts are always executed in a unix environment you can achieve the same as in @NearHuscarl answer, but with fewer lines of code by initializing your .env dotenv file from a shell script. The generated .env is then picked up by the react-scripts in the subsequent step.

"scripts": {
  "start": "sh ./env.sh && react-scripts start"
  "build": "sh ./env.sh && react-scripts build",
}

where .env.sh is placed in your project root and contains code similar to the one below to override you .env file content on each build or start.

{
  echo BROWSER=none
  echo REACT_APP_FOO=bar
  echo REACT_APP_VERSION=$(git rev-parse --short HEAD)
  echo REACT_APP_APP_BUILD_DATE=$(date)
  # ...
} > .env

Since the .env is overridden on each build, you may consider putting it on the .gitignore list to avoid too much noise in your commit diffs.

Again the disclaimer: This solution only works for environments where a bourne shell interpreter or similar exists, i.e. unix.

Wallacewallach answered 24/8, 2021 at 12:12 Comment(0)
L
0

You can easily inject your git information like commit hash into your index.html using CRACO and craco-interpolate-html-plugin. Such way you won't have to use yarn eject and it also works for development server environment along with production builds.

After installing CRACO the following config in craco.config.js worked for me:

const interpolateHtml = require('craco-interpolate-html-plugin');

module.exports = {
  plugins: [
    {
      plugin: interpolateHtml,
      // Enter the variable to be interpolated in the html file
      options: {
        BUILD_VERSION: require('child_process')
          .execSync('git rev-parse HEAD', { cwd: __dirname })
          .toString().trim(),
      },
    },
  ],
};

and in your index.html: <meta name="build-version" content="%BUILD_VERSION%" />

Here are the lines of code to add in package.json to make it all work:

"scripts": {
    "start": "craco start",
    "build": "craco build"
}
Leland answered 20/1, 2022 at 17:15 Comment(0)
I
0

The answers provided above are covering a wide range of scenarios, but I couldn't find any that offers a vanilla JS and cross-platform solution. @NearHuscarl's solution overwrites entirely the content of .env, which is not convinient when it contains other values. Here's a solution that only updates the file.
I define a new script which calls my JS snippet before my usual react-scripts commands :

package.json

{
    "scripts": {
        "git-version": "node scripts/set_env.js",
        "start": "yarn run git-version && react-scripts start",
        "build": "yarn run git-version && react-scripts build",
        
    },
}

The script itself is run with Node and requires no external dependencies (apart from having a Node environment). It will update the .env file with the REACT_APP_VERSION=XX property added or updated. The original content is kept as is. If the file does not exist it will be created.

scripts/set_env.js

const childProcess = require("child_process");
const fs = require("fs");
const os = require("os");
const readline = require("readline");

const PREFIX = "REACT_APP_VERSION=";

// Append current git version on .env file https://mcmap.net/q/826197/-add-git-information-to-create-react-app
function writeToEnv(content) {
    return new Promise((resolve, reject) => {
        const fileStream = fs.createReadStream(".env");
        let output = "";
        let hasVersion = false;

        const readInterface = readline.createInterface({
            input: fileStream,
            crlfDelay: Infinity,
        });

        fileStream.on("error", (err) => {
            readInterface.close();
            fileStream.close();
            reject(err);
        });

        readInterface.once("error", (err) => {
            readInterface.close();
            fileStream.close();
            if (err.includes("Error: ENOENT:")) {
                console.info(".env file not found, will create it", err);
            } else {
                reject(err);
            }
        });

        readInterface.on("line", (line) => {
            let lineToAppend = "";

            if (!hasVersion && line.startsWith(PREFIX)) {
                lineToAppend = content;
                hasVersion = true;
            } else {
                lineToAppend = line;
            }

            output += lineToAppend + (lineToAppend.endsWith(os.EOL) ? undefined : os.EOL);
        });

        readInterface.on("close", () => {
            if (!hasVersion) {
                // File was not found
                output += content + os.EOL;
            }
            fs.writeFile(".env", output, (error) => {
                if (error) {
                    reject(error);
                } else {
                    resolve();
                }
            });
        });
    });
}

(async () => {
    try {
        const tags = childProcess.execSync("git describe --tags").toString().trim();
        const reactVersion = `${PREFIX}${tags}`;
        console.info(`Setting ${reactVersion}`);
        await writeToEnv(reactVersion);
    } catch (err) {
        console.error(`Could not set ${PREFIX}`, err);
    }
})();

Isosceles answered 19/9, 2023 at 9:46 Comment(0)
K
0

This answer is a blend of many already here, but works for my Next.js setup deployed on Vercel.

My company needed a way to match our deployments with user-submitted bug reports and analytics, so the simplest way I could think of is to place a hidden git hash on pages, which we can easily scrape and/or use directly during user-submissions.

Add build script in package.json

Simply add a script called git-hash in the scripts section of your package.json, e.g.

  "scripts": {
    "commit-hash": "echo \"export const sha='$(git rev-parse HEAD)'\" > src/lib/commit-hash.js",
    ...
  }

Then use the commit-hash script in your other scripts, as needed, e.g.

  "scripts": {
    "commit-hash": "echo \"export const sha='$(git rev-parse HEAD)'\" > src/lib/commit-hash.js",
    "dev": "yarn run commit-hash && NODE_OPTIONS='--inspect' next dev",
    "build": "yarn run commit-hash && next build",
    "start": "yarn run commit-hash && next start",
    "lint": "yarn run commit-hash && next lint",
  }

Replace yarn with npm or whatever you use...

This writes a JavaScript file to the src/lib/ directory called commit-hash.js that contains a named export sha.

Import file from within source code

We can use commit-hash.js in other files with a simple import { sha } from 'path/to/src/lib/commit-hash' statement.

As an example, I import the named export in a Next.js 14 app, e.g.

import { sha } from '@/lib/commit-hash'

export default function Page() {
  return <MyComponent sha={sha} />
}

Use git hash in markup

And once used in a component, the markup generated, for example, looks like

A snippet of Firefox developer tools displaying markup of the sha example above

You can clearly see that the hash 9986ada457765d4b0fcfc097f61b96b4fa688577 is now rendered in the HTML markup.

Kutz answered 20/6 at 17:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.