前言

安装与使用

安装与常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 全局安装Rush
npm install -g @microsoft/rush

//创建一个项目文件夹
mkdir xxx
cd xxx

// 项目初始化
rush init

// 任意目录下
rush install 安装依赖

rush update:当代码依赖发生变化时执行

rush build:编译代码

rush purge:删除 rush 相关的 temp 文件

// 指定项目编译pkg-name为rush.json中配置的packageName

rush build --to pkg-name

changelog generation

Create rush-changemanager autoinstaller

1
2
3
4
5
6
7
8
9
10
rush init-autoinstaller --name rush-changemanager
cd common/autoinstallers/rush-changemanager

pnpm add @microsoft/rush-lib
pnpm add @rushstack/node-core-library
pnpm add gitlog
pnpm add recommended-bump
# When you are finished, run this command to ensure that the
# common/autoinstallers/rush-commitizen/ppnpm-lock.yaml file is up to date
rush update-autoinstaller --name rush-changemanager

修改common/config/rush/command-line.json

1
2
3
4
5
6
7
8
9
10
11
12
13
{
//...
"commands": [
{
"name": "changefiles",
"commandKind": "global",
"summary": "",
"autoinstallerName": "rush-changemanager",
"shellCommand": "node common/scripts/rush-changefiles.js"
}
],
//...
}

创建/common/scripts/rush-changefiles.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
const child_process = require('child_process');
const path = require('path');
const fs = require('fs');
const node_modules = path.join(__dirname, '..', 'autoinstallers/rush-changemanager/node_modules');
const rushLib = require(path.join(node_modules, '@microsoft/rush-lib'));
const rushCore = require(path.join(node_modules, '@rushstack/node-core-library'));
const gitlog = require(path.join(node_modules, 'gitlog')).default;
const recommendedBump = require(path.join(node_modules, 'recommended-bump'));

const Colors = {
Red: '\u001B[31m',
Purple: '\u001B[35m',
Green: '\u001B[32m',
Yellow: '\u001B[33m',
Reset: '\u001B[0m'
};

function executeCommand(command) {
//stdio: 'inherit': process will use the parent's stdin, stdout and stderr streams
return child_process.execSync(command, { stdio: 'inherit' });
}
function executeCommandAsync(command) {
//stdio: 'inherit': process will use the parent's stdin, stdout and stderr streams
return child_process.exec(command, { stdio: 'inherit' });
}
function getCurrentBranch() {
const currBranch = child_process.execSync("git branch --show-current").toString().trim();
return child_process.execSync(`git rev-parse --symbolic-full-name --abbrev-ref "${currBranch}@{u}"`).toString().trim();
}
function parseLastCommit(repoPath) {
const lastCommit = gitlog({ repo: repoPath, file: repoPath, number: 1, fields: ["subject", "body", "rawBody", "authorEmail", "hash"] });
//fix, feat or BREAKING?
const { increment } = recommendedBump([lastCommit[0].rawBody]);
if (increment) {
return {
increment: increment,
subject: lastCommit[0].subject,
emailAddress: lastCommit[0].authorEmail,
lastMessage: lastCommit[0].body,
hash: lastCommit[0].hash,
}
}
else {
return false;
}

}
function parseRecentCommits(projectName, projectPath, lastCommitInfo, repoPath, defaultCommitMessage) {

const commits = gitlog({repo: repoPath,file: projectPath,number: 2,fields:["subject", "body", "rawBody", "authorEmail", "hash"]});
//if the last two messages are the same, skip change file generation
const commitMsgPass = (commits.length == 2 && commits[0].rawBody != commits[1].rawBody || commits.length == 1) && commits[0].body!= defaultCommitMessage;

//The project was included in the last commit
if (lastCommitInfo.hash == commits[0].hash && commitMsgPass) {
return Object.assign(lastCommitInfo, {
projectName: projectName,
});
}
//no changes for this project were included in the last commit, or the last 2 commits identical
else {
return false;
}
}
async function getChangedProjectNamesAsync(rushConfiguration) {
const projectAnalyzer = new rushLib.ProjectChangeAnalyzer(rushConfiguration);
const terminal = new rushCore.Terminal(new rushCore.ConsoleTerminalProvider({ verboseEnabled: false }));

try {
const changedProjects = await projectAnalyzer.getChangedProjectsAsync({
targetBranchName: getCurrentBranch() , //rushConfiguration.repositoryDefaultFullyQualifiedRemoteBranch,
terminal: terminal,
enableFiltering: false,
shouldFetch: true,
includeExternalDependencies: false
});
let rushProjects = new Map()
//TODO: parse consumers? project.consumingProjects

changedProjects.forEach(project => {
rushProjects.set(project.packageName, project.projectFolder);
});
return rushProjects;

} catch (error) {
console.log(error);
return null;
}
}
function generateChangeFile(rushConfig, res) {
let changeFilePath = rushLib.ChangeManager.createEmptyChangeFiles(rushConfig, res.projectName, res.emailAddress);
const file = require(changeFilePath);
file.changes[0].comment = res.lastMessage;
file.changes[0].type = res.increment;
fs.writeFileSync(changeFilePath, JSON.stringify(file, null,2));
}

function generateChangeFilesFromCommit() {
const rushConfiguration = rushLib.RushConfiguration.loadFromDefaultLocation({ startingFolder: process.cwd() });
//parse last commit to see if change file is necessary
const lastCommitInfo = parseLastCommit(rushConfiguration.rushJsonFolder);
if (lastCommitInfo) {
//get changed projects managed by rush
getChangedProjectNamesAsync(rushConfiguration).then((rushProjects) => {
rushProjects.forEach((value, key) => {
//parse last 2 commits: was last commit for the project the last hash?
const result = parseRecentCommits(key, value, lastCommitInfo, rushConfiguration.rushJsonFolder, rushConfiguration.gitChangeLogUpdateCommitMessage);
if (result) {
console.log(Colors.Green + `Generating change file for "${result.increment}": "${result.subject}" form project ${result.projectName}` + Colors.Reset);
generateChangeFile(rushConfiguration, result);
console.log(Colors.Green + "Automatically adding change files" + Colors.Reset);
executeCommand(`git add ${rushConfiguration.changesFolder}`);
console.log(Colors.Green + `Commiting change files with message: "${rushConfiguration.gitChangeLogUpdateCommitMessage}"` + Colors.Reset);
executeCommandAsync(`git commit --no-edit --no-verify --amend `);
console.log(Colors.Green + "All done!" + Colors.Reset);
}
else {
console.log(Colors.Yellow + "Change file not required." + Colors.Reset);
}
});
});
}
}

generateChangeFilesFromCommit();