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?
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?
As suggested by @Yuri, I was able to solve this by using npm
scripting.
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'}
);
});
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",
...
}
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",
...
}
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.
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.
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"
}
npm run build.prod
suggestions for code improvement are welcome :)
I have a little different approach, inspired by answers repo Seba Arce and Jeroen , in main project directory:
npm install git-rev-sync --save
(this lib give acces to hash and branch name)git-version-gen.js
with following bodyconst 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);
package.json
in scripts
add "build": "node git-version-gen.js && ng build ..."
app.component.ts
use it as followsimport { 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)
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.
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
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
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.
/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
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