Vilmantas Baranauskas
Vilmantas Baranauskas

Reputation: 6726

How to include git revision into angular-cli application?

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?

Upvotes: 47

Views: 31930

Answers (9)

Saurabh Gangamwar
Saurabh Gangamwar

Reputation: 802

  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 :)

Upvotes: 18

Vilmantas Baranauskas
Vilmantas Baranauskas

Reputation: 6726

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",
    ...
}

Upvotes: 58

Charly
Charly

Reputation: 1139

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

Upvotes: 10

repo
repo

Reputation: 760

I've done it by generating prebuild script run on postinstall and run before any angular related script

const fs =  require('fs');
const git = require('git-rev-sync');
var mkdirp = require('mkdirp');

const releaseTag = git.tag();
const template = `export const gitTag = '${releaseTag}';\n`;

mkdirp('./generated', function(err) {
    fs.writeFileSync('./generated/git-tag.ts', template, { encoding: 'UTF-8' });
});

which generated git-tag.ts file:

export const gitTag = 'xxxxxxx';

and now u just use in component

import { gitTag } from '[pathToRoot]/generated/git-tag';

also add .gitignore

generated

Upvotes: 2

Valerio
Valerio

Reputation: 2590

I went with a modified version of Vilmantas Baranauskas

I moved src/index.html to src/index.base.html and added an empty <meta name="revision" content=""> inside the HEAD

Example:

<head>
    <meta charset="utf-8">
    <title>MySuperAwesome Angular</title>
    <base href="/">

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="revision" content="">
    <link rel="icon" type="image/x-icon" href="favicon.ico">

Then modified git.version.ts like this:

import 'rxjs/add/observable/combineLatest';

import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import { Observable } from 'rxjs/Observable';

const indexBasePath = join(__dirname, 'src');

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

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

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

Observable
    .combineLatest(revObs, branchObs)
    .subscribe(([revision, branch]) => {
        console.log(`revision: '${revision}', branch: '${branch}'`);

        const baseHTML = readFileSync(join(indexBasePath, 'index.base.html'), 'utf8');
        const html = baseHTML
          .replace('<meta name="revision" content="">', `<meta name="revision" content="${ revision }">`);

        writeFileSync(
          join(indexBasePath, 'index.html'),
          html,
          { encoding: 'utf8' }
        );
    });

In this example I only put revision, but you can be more thorough and put branch and version inside your html HEAD section

Upvotes: 2

Kamil Kiełczewski
Kamil Kiełczewski

Reputation: 92567

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)

Upvotes: 8

Jeroen
Jeroen

Reputation: 63790

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.

Upvotes: 26

Seba Arce
Seba Arce

Reputation: 41

For angular 6

1 Install git-describe as a dev dependency

 npm i git-describe -s

2 On your root project create a grab-git-info.js

   const { gitDescribeSync } = require('git-describe');
   const { writeFileSync } = require('fs');
   const path = require('path');
   const info = gitDescribeSync();
   const infoJson = JSON.stringify(info, null, 2);
   writeFileSync(path.join(__dirname, '/src/git-version.json'), infoJson);

The output of the grab-git-info.js script will be the ‘git-version.json’ file under /src/ which will contain all the git info needed by our app.

In order to be able to import the json file (or any other json file) we need to add a definition file declaring of the added module so that the Typescript compiler will recognize it.

  1. Under your /src create typings.d.ts file (read more about typings.d.ts here: https://angular.io/guide/typescript-configuration#typescript-typings)

/src/typings.d.ts:

 declare module '*.json' {
   const value: any;
   export default value;
 }

From this point on you can import any json file located under /src as a module!

In your component you can import this json

 import * as data from '../../../git-version.json';
 ...
 public git = data;

In the html

 Rev: {{git.hash}}

Finally Add and most important, run the script before build

In package.json add:

"scripts": {
  "ng": "ng",
  "start": "ng serve",
  "build": "node grab-git-info && ng build",

And run the app with

 npm run build

Upvotes: 2

abahet
abahet

Reputation: 10633

Use gulp task using gulp-replace and git-rev-sync to add the hash and branch on build :

1) Create the gulp task

var gulp            =    require('gulp'),
    replace         =    require('gulp-replace'),
    git             =    require('git-rev-sync'),

gulp.task('git', function () {
    gulp.src('src/index.html')
        .pipe(replace('{{git-branch}}', git.branch()))
        .pipe(replace('{{git-hash}}', git.short()))
        .pipe(gulp.dest('src/'))
});

// Build Tasks
gulp.task('build', ['git']);

2) Add the following code to index.html :

{{git-branch}}@{{git-hash}}

3) Run

gulp build

Upvotes: 0

Related Questions