上图是页面上展示的测试环境/开发环境
版本信息。
后面有介绍
上图表示的是每次提交的Git Commit
的信息,当然,这里我是每次提交都记录,你可以在每次构建的时候记录。
So,我们接下来用 Angular
实现下效果,React
和 Vue
同理。
搭建环境
因为这里的重点不是搭建环境,我们直接用 angular-cli
脚手架直接生成一个项目就可以了。
Step 1: 安装脚手架工具
代码语言:javascript复制npm install -g @angular/cli
Step 2: 创建一个项目
代码语言:javascript复制# ng new PROJECT_NAME
ng new ng-commit
Step 3: 运行项目
代码语言:javascript复制npm run start
项目运行起来,默认监听4200
端口,直接在浏览器打开http://localhost:4200/
就行了。
4200 端口没被占用的前提下
此时,ng-commit
项目重点文件夹 src
的组成如下:
src
├── app // 应用主体
│ ├── app-routing.module.ts // 路由模块
│ .
│ └── app.module.ts // 应用模块
├── assets // 静态资源
├── main.ts // 入口文件
.
└── style.less // 全局样式
上面目录结构,我们后面会在 app
目录下增加 services
服务目录,和 assets
目录下的 version.json
文件。
记录每次提交的信息
在根目录创建一个文件version.txt
,用于存储提交的信息;在根目录创建一个文件commit.js
,用于操作提交信息。
重点在commit.js
,我们直接进入主题:
const execSync = require('child_process').execSync;
const fs = require('fs')
const versionPath = 'version.txt'
const buildPath = 'dist'
const autoPush = true;
const commit = execSync('git show -s --format=%H').toString().trim(); // 当前的版本号
let versionStr = ''; // 版本字符串
if(fs.existsSync(versionPath)) {
versionStr = fs.readFileSync(versionPath).toString() 'n';
}
if(versionStr.indexOf(commit) != -1) {
console.warn('x1B[33m%sx1b[0m', 'warming: 当前的git版本数据已经存在了!n')
} else {
let name = execSync('git show -s --format=%cn').toString().trim(); // 姓名
let email = execSync('git show -s --format=�').toString().trim(); // 邮箱
let date = new Date(execSync('git show -s --format=�').toString()); // 日期
let message = execSync('git show -s --format=%s').toString().trim(); // 说明
versionStr = `git:${commit}n作者:${name}<${email}>n日期:${date.getFullYear() '-' (date.getMonth() 1) '-' date.getDate() ' ' date.getHours() ':' date.getMinutes()}n说明:${message}n${new Array(80).join('*')}n${versionStr}`;
fs.writeFileSync(versionPath, versionStr);
// 写入版本信息之后,自动将版本信息提交到当前分支的git上
if(autoPush) { // 这一步可以按照实际的需求来编写
execSync(`git add ${ versionPath }`);
execSync(`git commit ${ versionPath } -m 自动提交版本信息`);
execSync(`git push origin ${ execSync('git rev-parse --abbrev-ref HEAD').toString().trim() }`)
}
}
if(fs.existsSync(buildPath)) {
fs.writeFileSync(`${ buildPath }/${ versionPath }`, fs.readFileSync(versionPath))
}
上面的文件可以直接通过 node commit.js
进行。为了方便管理,我们在 package.json
上加上命令行:
"scripts": {
"commit": "node commit.js"
}
那样,使用 npm run commit
同等 node commit.js
的效果。
生成版本信息
有了上面的铺垫,我们可以通过 commit
的信息,生成指定格式的版本信息version.json
了。
在根目录中新建文件version.js
用来生成版本的数据。
const execSync = require('child_process').execSync;
const fs = require('fs')
const targetFile = 'src/assets/version.json'; // 存储到的目标文件
const commit = execSync('git show -s --format=%h').toString().trim(); //当前提交的版本号,hash 值的前7位
let date = new Date(execSync('git show -s --format=�').toString()); // 日期
let message = execSync('git show -s --format=%s').toString().trim(); // 说明
let versionObj = {
"commit": commit,
"date": date,
"message": message
};
const data = JSON.stringify(versionObj);
fs.writeFile(targetFile, data, (err) => {
if(err) {
throw err
}
console.log('Stringify Json data is saved.')
})
我们在 package.json
上加上命令行方便管理:
"scripts": {
"version": "node version.js"
}
根据环境生成版本信息
针对不同的环境生成不同的版本信息,假设我们这里有开发环境 development
,生产环境 production
和车测试环境 test
。
- 生产环境版本信息是
major.minor.patch
,如:1.1.0 - 开发环境版本信息是
major.minor.patch:beta
,如:1.1.0:beta - 测试环境版本信息是
major.minor.path-data:hash
,如:1.1.0-2022.01.01:4rtr5rg
方便管理不同环境,我们在项目的根目录中新建文件如下:
代码语言:javascript复制config
├── default.json // 项目调用的配置文件
├── development.json // 开发环境配置文件
├── production.json // 生产环境配置文件
└── test.json // 测试环境配置文件
相关的文件内容如下:
代码语言:javascript复制// development.json
{
"env": "development",
"version": "1.3.0"
}
代码语言:javascript复制// production.json
{
"env": "production",
"version": "1.3.0"
}
代码语言:javascript复制// test.json
{
"env": "test",
"version": "1.3.0"
}
default.json
根据命令行拷贝不同环境的配置信息,在 package.json
中配置下:
"scripts": {
"copyConfigProduction": "cp ./config/production.json ./config/default.json",
"copyConfigDevelopment": "cp ./config/development.json ./config/default.json",
"copyConfigTest": "cp ./config/test.json ./config/default.json",
}
Is easy Bro, right?
整合生成版本信息的内容,得到根据不同环境生成不同的版本信息,具体代码如下:
代码语言:javascript复制const execSync = require('child_process').execSync;
const fs = require('fs')
const targetFile = 'src/assets/version.json'; // 存储到的目标文件
const config = require('./config/default.json');
const commit = execSync('git show -s --format=%h').toString().trim(); //当前提交的版本号
let date = new Date(execSync('git show -s --format=�').toString()); // 日期
let message = execSync('git show -s --format=%s').toString().trim(); // 说明
let versionObj = {
"env": config.env,
"version": "",
"commit": commit,
"date": date,
"message": message
};
// 格式化日期
const formatDay = (date) => {
let formatted_date = date.getFullYear() "." (date.getMonth() 1) "." date.getDate()
return formatted_date;
}
if(config.env === 'production') {
versionObj.version = config.version
}
if(config.env === 'development') {
versionObj.version = `${ config.version }:beta`
}
if(config.env === 'test') {
versionObj.version = `${ config.version }-${ formatDay(date) }:${ commit }`
}
const data = JSON.stringify(versionObj);
fs.writeFile(targetFile, data, (err) => {
if(err) {
throw err
}
console.log('Stringify Json data is saved.')
})
在 package.json
中添加不同环境的命令行:
"scripts": {
"build:production": "npm run copyConfigProduction && npm run version",
"build:development": "npm run copyConfigDevelopment && npm run version",
"build:test": "npm run copyConfigTest && npm run version",
}
生成的版本信息会直接存放在 assets
中,具体路径为 src/assets/version.json
。
结合 Angular 在页面中展示版本信息
最后一步,在页面中展示版本信息,这里是跟 angular
结合。
使用 ng generate service version
在 app/services
目录中生成 version
服务。在生成的 version.service.ts
文件中添加请求信息,如下:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class VersionService {
constructor(
private http: HttpClient
) { }
public getVersion():Observable<any> {
return this.http.get('assets/version.json')
}
}
要使用请求之前,要在 app.module.ts
文件挂载 HttpClientModule
模块:
import { HttpClientModule } from '@angular/common/http';
// ...
imports: [
HttpClientModule
],
之后在组件中调用即可,这里是 app.component.ts
文件:
import { Component } from '@angular/core';
import { VersionService } from './services/version.service'; // 引入版本服务
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.less']
})
export class AppComponent {
public version: string = '1.0.0'
constructor(
private readonly versionService: VersionService
) {}
ngOnInit() {
this.versionService.getVersion().subscribe({
next: (data: any) => {
this.version = data.version // 更改版本信息
},
error: (error: any) => {
console.error(error)
}
})
}
}
至此,我们完成了版本信息。我们最后来调整下 package.json
的命令:
"scripts": {
"start": "ng serve",
"version": "node version.js",
"commit": "node commit.js",
"build": "ng build",
"build:production": "npm run copyConfigProduction && npm run version && npm run build",
"build:development": "npm run copyConfigDevelopment && npm run version && npm run build",
"build:test": "npm run copyConfigTest && npm run version && npm run build",
"copyConfigProduction": "cp ./config/production.json ./config/default.json",
"copyConfigDevelopment": "cp ./config/development.json ./config/default.json",
"copyConfigTest": "cp ./config/test.json ./config/default.json"
}
使用 scripts
一是为了方便管理,而是方便 jenkins
构建方便调用。对于 jenkins
部分,感兴趣者可以自行尝试。
参考&后话
- node读取git信息
- NG-ZORRO
- 文章首发 - github blog