How to include git revision into angular-cli application?

23,121

Solution 1

As suggested by @Yuri, I was able to solve this by using npm scripting.

  1. Defined git.version.ts in the root of the angular-cli project:
import { Observable, combineLatest } from 'rxjs'

declare var require: any;
declare var process: any;

const fs = require('fs');
const exec = require('child_process').exec;

const revision = new Observable<string>(s => {
    exec('git rev-parse --short HEAD',
        (error: Error, stdout, stderr) => {
            if (error !== null) {
                console.log('git error: ' + error + stderr);
            }
            s.next(stdout.toString().trim());
            s.complete();
        });
});

const branch = new Observable<string>(s => {
    exec('git rev-parse --abbrev-ref HEAD',
        (error: Error, stdout, stderr) => {
            if (error !== null) {
                console.log('git error: ' + error + stderr);
            }
            s.next(stdout.toString().trim());
            s.complete();
        });
});

combineLatest(revision, branch)
    .subscribe(([revision, branch]) => {
        console.log(`version: '${process.env.npm_package_version}', revision: '${revision}', branch: '${branch}'`);

        const content = '// this file is automatically generated by git.version.ts script\n' +
            `export const versions = {version: '${process.env.npm_package_version}', revision: '${revision}', branch: '${branch}'};`;

        fs.writeFileSync(
            'src/environments/versions.ts',
            content,
            {encoding: 'utf8'}
        );
    });

  1. Added pre-build hook in package.json:
"scripts": {
    "ng": "ng",
    ...
    "start": "ng serve --proxy proxy-config.json",
    "prebuild.prod": "ts-node -O \"{\\\"module\\\":\\\"commonjs\\\"}\" git.version.ts",
    "build.prod": "ng build -prod",
    ...
}
  1. Use the generated src/environments/versions.ts in the application.

    UPDATE 10/2018: Here is the more-readable version of the script, rxjs-version-agnostic:

import { writeFileSync } from 'fs';
import { dedent } from 'tslint/lib/utils';
import { promisify } from 'util';
import * as child from 'child_process';
const exec = promisify(child.exec);

async function createVersionsFile(filename: string) {
  const revision = (await exec('git rev-parse --short HEAD')).stdout.toString().trim();
  const branch = (await exec('git rev-parse --abbrev-ref HEAD')).stdout.toString().trim();

  console.log(`version: '${process.env.npm_package_version}', revision: '${revision}', branch: '${branch}'`);

  const content = dedent`
      // this file is automatically generated by git.version.ts script
      export const versions = {
        version: '${process.env.npm_package_version}',
        revision: '${revision}',
        branch: '${branch}'
      };`;

  writeFileSync(filename, content, {encoding: 'utf8'});
}

createVersionsFile('src/environments/versions.ts');


Note, when using angular-cli v7.0.6, I also had to change script invocation in the package.json:

"scripts": {
    ...
    "prebuild.prod": "ts-node -O '{\"module\": \"commonjs\"}' git.version.ts",
    ...
}

Solution 2

The other answers were helpful, but I preferred a more simple, direct approach. Here's mine.

Run npm install git-describe --save-dev. Then add git-version.js to the root:

// This script runs operations *synchronously* which is normally not the best
// approach, but it keeps things simple, readable, and for now is good enough.

const { gitDescribeSync } = require('git-describe');
const { writeFileSync } = require('fs');

const gitInfo = gitDescribeSync();
const versionInfoJson = JSON.stringify(gitInfo, null, 2);

writeFileSync('git-version.json', versionInfoJson);

Optionally you can add /git-version.json to your .gitignore file.

Update your package.json to do something like this:

"scripts": {
  "build": "node git-version.js && ng build"
}

Then add version-info.ts to the root of your project:

export const versionInfo = (() => {
  try {
    // tslint:disable-next-line:no-var-requires
    return require('../../git-version.json');
  } catch {
    // In dev the file might not exist:
    return { tag: 'v0.0.0', hash: 'dev' };
  }
})();

And import the versionInfo in your app.component.ts or anywhere else you'd want to use it.

Solution 3

  1. Add git-version.js to the root.This code will execute git commands and write the output to the git-version.json file.
const childProcess = require('child_process');
const { writeFileSync } = require('fs');

const longSHA = childProcess.execSync("git rev-parse HEAD").toString().trim();
const shortSHA = childProcess.execSync("git rev-parse --short HEAD").toString().trim();
const branch = childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
const authorName = childProcess.execSync("git log -1 --pretty=format:'%an'").toString().trim();
const commitTime = childProcess.execSync("git log -1 --pretty=format:'%cd'").toString().trim();
const commitMsg = childProcess.execSync("git log -1 --pretty=%B").toString().trim();
const totalCommitCount = childProcess.execSync("git rev-list --count HEAD").toString().trim();

const versionInfo = {
    shortSHA: shortSHA,
    SHA : longSHA,
    branch: branch,
    lastCommitAuthor: authorName,
    lastCommitTime: commitTime,
    lastCommitMessage: commitMsg,
    lastCommitNumber: totalCommitCount
}

const versionInfoJson = JSON.stringify(versionInfo, null, 2);

writeFileSync('/src/git-version.json', versionInfoJson);

This code will generate the git-version.json file :-

{
  "shortSHA": "0e786d4",
  "SHA": "0e786d4ad3778463f6f30c28f254cc85c24eb4b3",
  "branch": "master",
  "lastCommitAuthor": "'saurabh'",
  "lastCommitTime": "'Thu Apr 9 12:59:16 2020 +0530'",
  "lastCommitMessage": "Commit message",
  "lastCommitNumber": "200"
}

Modify above code as per your requirements.

Run: node git-version.js This will generate the git-version.json into src directory.

  1. To run this code before or after the build. add a new script to package.json
"scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "build.prod": "node git-version.js && ng build --prod"
  }
  1. Run:- npm run build.prod

suggestions for code improvement are welcome :)

Solution 4

I like to keep things simple. Can add to your index.html:

<script>window.version = '{git-hash}';</script>

Then add a postbuild script to your package.json:

"postbuild": "sed -i '' \"s/{git\\-hash}/$(git rev-parse --short HEAD)/g\" dist/*/index.html"

Not elegant by any means. Personally I create an object on window with various information about the build (time, version, and a release summary link).

To stay more 'pure', stick the {git-hash} string in environment.prod.ts and run the sed against all main-*.js files produced.

"postbuild": "sed -i '' \"s/{git\\-hash}/$(git rev-parse --short HEAD)/g\" dist/*/main-*.js"

Note you'll always see '{git-hash}' running locally as it's only replaced post-build. If you replace it before the build, obviously can't replace it on future builds locally and would most definitely accidentally check that in.

---- UPDATE ----

Ended up creating a library to pull various information. Only ended up using version, build time, and commitTime personally.

https://www.npmjs.com/package/@rippell/ngx-build-info

Solution 5

Display branch name and commit hash

I have a little different approach, inspired by answers repo Seba Arce and Jeroen , in main project directory:

  • execute: npm install git-rev-sync --save (this lib give acces to hash and branch name)
  • add file git-version-gen.js with following body

const git  = require('git-rev-sync');
const { writeFileSync } = require('fs');

const gitInfo = { commit: git.short(), commitLong: git.long(), branch: git.branch() };
const ts = 'export const gitVersion = ' + JSON.stringify(gitInfo, null, 2);

writeFileSync('src/environments/git-version.ts', ts);
  • in package.json in scripts add "build": "node git-version-gen.js && ng build ..."
  • in your main app file e.g. app.component.ts use it as follows

import { gitVersion } from '../../../environments/git-version';

// ...

  constructor() {
    console.log(`GIT branch:`,gitVersion.branch);
    console.log(`GIT commit:`,gitVersion.commit);
  }

What are advantages of use this?

  • we create here src/environments/git-version.ts file so this is TypeScript, not .json - similar like you environments files - this will turn on support from your code editor (e.g VSCode)

  • you have acces to commit hash nad branch name (lib git-describe not gives acces to branch name)

  • if you commit generated git-version.ts file instead put it to .gitignore then project will run without build (e.g. by ng serve ...) and fresh developers will not be confused that there is missing some 'mystic' file... - however choice is up to you.

  • cross platform - tested on Azure (windows), MacOs (~linux-like)

Share:
23,121
gwentech
Author by

gwentech

Updated on July 21, 2022

Comments

  • gwentech
    gwentech almost 2 years

    I need to display git revision on my angular2 application's about page. The project is based on angular-cli.

    How can build be extended so git revision is put for example into environment.ts or other place accessible to application?

  • George C.
    George C. about 6 years
    ts-node git.version.ts command does not work and I don't know why I geting this: Did you mean one of these? rebuild prefix profile
  • Cameron Yick
    Cameron Yick over 5 years
    For anyone coming to this using angular 6 / rxjs 6: change the import to import { Observable, combineLatest } from 'rxjs';, and then replace Observable.combineLatest with just combineLatest.
  • thouliha
    thouliha over 5 years
    This was a beautiful solution, but I also suggest adding the tag: git describe --abbrev=0 --tags
  • kinjelom
    kinjelom over 5 years
    npm ERR! notsup Unsupported platform for [email protected]: wanted {"os":"linux","arch":"any"} (current: {"os":"win32","arch":"x64"})
  • Fitrah M
    Fitrah M over 5 years
    How to generate the src/environments/versions.ts file? I call the ng prebuild.prod command but nothing happens. I wait for several minutes but no output. My versions: bash Angular CLI: 6.0.8 Node: 8.11.1 OS: win32 x64 Angular: 5.2.10 Package Version ----------------------------------------------------------- @angular/cli 6.0.8 @angular/pwa 0.6.5 @angular/service-worker 5.2.11 @ngtools/webpack 6.0.5 rxjs 5.5.10
  • Jeroen
    Jeroen over 5 years
    Strange. I ran this on my Windows 10 x64 machine without problems, and it runs fine on AppVeyor's VS2017 image too.
  • oded
    oded over 5 years
    this is a very clean and nice way to acheive it!!! i thought sharing a code that i added to this solution that will print out more git information and jobname or BUILD_NUMBER if you are running it through jenkins
  • oded
    oded over 5 years
    here is the additional code: add 4 more observables lastCommitTime which is git log --format="%ai" -n1 HEAD, lastCommitMessage which is git log --format="%B" -n1 HEAD, lastCommitAuthor which is git log --format="%aN" -n1 HEAD, lastCommitNumber which is git rev-list --count HEAD, const commitSHA = process.env.GIT_COMMIT || branch; const buildInfo = process.env.BUILD_NUMBER || 'LOCAL ' + revision + ' - last commit number ' + lastCommitNumber + ', by ' + lastCommitAuthor + ' on ' + lastCommitTime; const jobName = process.env.JOB_NAME || 'local build'
  • codingbadger
    codingbadger over 5 years
    @wildloop The npm syntax to install git-describe should be npm install git-describe --save-dev
  • Kamil Kiełczewski
    Kamil Kiełczewski over 4 years
    this is probably only for systems which have sed command (linux:Yes, windows: No)
  • Charly
    Charly over 4 years
    Absolutely true, partially why I ended up creating the npm library
  • MIWMIB
    MIWMIB almost 4 years
    -O '{\"module\": \"commonjs\"}' in package.json didn't work for me (win10). I had to remove it and change the imports in git.version.ts to require statements.
  • David
    David over 3 years
    This worked great, my only critique is that you misspelled the variable chidProcess
  • Saurabh Gangamwar
    Saurabh Gangamwar over 3 years
    Thanks for letting me know.fixed that issue
  • Ivan Gavlik
    Ivan Gavlik over 3 years
    what is require('fs'), require('git-rev-sync') and require('mkdirp') do i need to install them
  • theo
    theo over 3 years
    nice one! You can add version: process.env.npm_package_version, to add the version in the json file produced.
  • theo
    theo over 3 years
    @MIWMIB yeah I had the same. At first it worked fine on my macos and build server (linux) but was failing on windoze with 'ts-node' is not recognized as an internal or external command, operable program or batch file