-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
177 lines (165 loc) · 4.47 KB
/
index.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
"use strict";
const fs = require("fs-extra");
const path = require("path");
const http = require("http");
const util = require("util");
const qs = require("query-string");
const exec = util.promisify(require("child_process").exec);
const logger = require("./log.js");
const git = require("./git.js");
const config = require("./config.json");
const port = config.port;
const localRepo = path.join(config.localRepo);
const secret = config.secret;
// 确保拉取代码的目录存在
function ensureLocalRepo() {
if (!fs.existsSync(localRepo)) {
fs.mkdirSync(localRepo);
}
}
// 处理git push hook请求
function handleRequest(req, res) {
ensureLocalRepo();
// 考虑url后面带参数来支持更多特性
const url = req.url;
let queryOptions, contextPath;
let index = url.indexOf("?");
if (index > 0) {
queryOptions = qs.parse(url.substring(index + 1));
contextPath = url.substring(0, index);
} else {
queryOptions = {};
contextPath = url;
}
let isValid = checkReqValid(req);
if (!isValid) {
res.writeHead(401, { "Content-Type": "text/plain" });
res.end("Secret token is invalid.");
return;
}
if (req.method == "POST") {
let data = "";
req.on("data", (chunk) => {
data += chunk;
});
req.on("end", async () => {
// 当请求结束时候,立刻回复git服务器
res.write("ok");
res.end();
data = decodeURI(data);
data = JSON.parse(data);
logger.info(data);
let repo = findRepository(data.repository);
logger.info(repo);
if (repo) {
// 先拉取代码,之后执行脚本,最后把结果拷贝到对应的目录
await fetchRepo(repo, queryOptions);
await execScript(repo);
await copyRepo(repo, queryOptions, contextPath);
logger.info("Finished.");
}
});
}
}
// 检查请求是否合法:校验secret
function checkReqValid(req) {
let headers = req.headers;
let token = headers["x-gitlab-token"];
if (token) {
return token === secret;
}
return true;
}
// 获取仓库在本地的地址
function getRepoLocalPath(repo) {
const url = repo.url;
const repoName = url.substring(
url.lastIndexOf("/") + 1,
url.lastIndexOf(".")
);
const repoPath = path.join(localRepo, repoName);
return repoPath;
}
// pull 或者 clone 仓库资源
async function fetchRepo(repo, options) {
const url = repo.url;
const repoPath = getRepoLocalPath(repo);
const branch = options.branch || repo.branch || "";
if (fs.existsSync(repoPath)) {
return await git
.pull(repoPath)
.then(() => {
logger.info("git pull finish");
})
.catch((error) => {
logger.error(error);
});
} else {
return await git
.clone(url, branch, localRepo)
.then(() => {
logger.info("git clone finish");
})
.catch((error) => {
logger.error(error);
});
}
}
// 从配置中找到对应的仓库
function findRepository(repo) {
let urls = [
repo["ssh_url"],
repo["clone_url"],
repo["git_ssh_url"],
repo["git_http_url"],
];
let result;
config.repository.map((item) => {
if (urls.includes(item.url)) {
result = item;
return;
}
});
return result;
}
// 拷贝仓库到指定的工作路径
async function copyRepo(repo, options, contextPath) {
const dir = options.dir || "";
let repoPath = getRepoLocalPath(repo);
// 拷贝指定dir
repoPath = path.join(repoPath, dir);
let deployPath = repo.deployPath;
if (deployPath) {
// 拼接上下文作为目录
deployPath = path.join(deployPath, contextPath);
if (!fs.existsSync(deployPath)) {
fs.mkdirsSync(deployPath);
}
logger.info(`copy ${repoPath} to ${deployPath}`);
fs.emptyDir(deployPath).then(() => {
fs.copy(repoPath, deployPath).then(() => console.log('success!')).catch(err => console.error(err))
});
}
}
// 拷贝完成后将会在对应的目录下执行的脚本
async function execScript(repo) {
// 只能在对应仓库下执行命令
let cmd = repo.command;
if (!cmd) return;
let repoPath = getRepoLocalPath(repo);
let options = {
cwd: repoPath,
maxBuffer: 10 * 1024 * 1024,
windowsHide: true,
};
const { error, stdout, stderr } = await exec(cmd, options);
if (error) {
logger.error(`exec error: ${error} ${stderr}`);
return;
}
logger.info(stdout);
}
const server = http.createServer(handleRequest);
server.listen(config.port, () => {
logger.info(`WebHook Server running on port ${port}`);
});