imooc-cli-dev-gd 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +0 -0
- package/commands/init/README.md +11 -0
- package/commands/init/__tests__/init.test.js +7 -0
- package/commands/init/lib/getProjectTemplate.js +7 -0
- package/commands/init/lib/index.js +423 -0
- package/commands/init/package.json +41 -0
- package/commands/publish/README.md +11 -0
- package/commands/publish/__tests__/publish.test.js +7 -0
- package/commands/publish/lib/index.js +67 -0
- package/commands/publish/package.json +32 -0
- package/core/cli/README.md +11 -0
- package/core/cli/__tests__/core.test.js +7 -0
- package/core/cli/bin/index.js +9 -0
- package/core/cli/lib/const.js +5 -0
- package/core/cli/lib/index.js +151 -0
- package/core/cli/package.json +41 -0
- package/core/exec/README.md +11 -0
- package/core/exec/__tests__/exec.test.js +7 -0
- package/core/exec/lib/index.js +89 -0
- package/core/exec/package.json +31 -0
- package/lerna.json +9 -0
- package/models/cloudbuild/README.md +11 -0
- package/models/cloudbuild/__tests__/cloudbuild.test.js +7 -0
- package/models/cloudbuild/lib/index.js +99 -0
- package/models/cloudbuild/package.json +28 -0
- package/models/command/README.md +11 -0
- package/models/command/__tests__/Command.test.js +7 -0
- package/models/command/lib/index.js +56 -0
- package/models/command/package.json +31 -0
- package/models/git/README.md +11 -0
- package/models/git/__tests__/git.test.js +7 -0
- package/models/git/lib/GitServer.js +60 -0
- package/models/git/lib/Gitee.js +59 -0
- package/models/git/lib/GiteeRequest.js +50 -0
- package/models/git/lib/Github.js +63 -0
- package/models/git/lib/GithubRequest.js +53 -0
- package/models/git/lib/index.js +512 -0
- package/models/git/package.json +35 -0
- package/models/package/README.md +11 -0
- package/models/package/__tests__/package.test.js +7 -0
- package/models/package/lib/index.js +121 -0
- package/models/package/package.json +35 -0
- package/package.json +14 -0
- package/utils/format-path/README.md +11 -0
- package/utils/format-path/__tests__/format-path.test.js +7 -0
- package/utils/format-path/lib/index.js +15 -0
- package/utils/format-path/package.json +26 -0
- package/utils/get-npm-info/README.md +11 -0
- package/utils/get-npm-info/__tests__/get-npm-info.test.js +7 -0
- package/utils/get-npm-info/lib/index.js +65 -0
- package/utils/get-npm-info/package.json +28 -0
- package/utils/log/README.md +11 -0
- package/utils/log/__tests__/log.test.js +7 -0
- package/utils/log/lib/index.js +10 -0
- package/utils/log/package.json +32 -0
- package/utils/request/README.md +11 -0
- package/utils/request/__tests__/request.test.js +7 -0
- package/utils/request/lib/index.js +22 -0
- package/utils/request/package.json +29 -0
- package/utils/utils/README.md +11 -0
- package/utils/utils/__tests__/utils.test.js +7 -0
- package/utils/utils/lib/index.js +77 -0
- package/utils/utils/package.json +26 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const SimpleGit = require('simple-git');
|
|
6
|
+
const fse = require('fs-extra');
|
|
7
|
+
const userHome = require('user-home');
|
|
8
|
+
const inquirer = require('inquirer');
|
|
9
|
+
const terminalLink = require('terminal-link');
|
|
10
|
+
const semver = require('semver');
|
|
11
|
+
const log = require('@imooc-cli-dev-gd/log');
|
|
12
|
+
const { readFile, writeFile, spinnerStart } = require('@imooc-cli-dev-gd/utils');
|
|
13
|
+
const Github = require('./Github');
|
|
14
|
+
const Gitee = require('./Gitee');
|
|
15
|
+
const CloudBuild = require('@imooc-cli-dev-gd/cloudbuild');
|
|
16
|
+
|
|
17
|
+
const DEFAULT_CLI_HOME = '.imooc-cli-dev';
|
|
18
|
+
const GIT_ROOT_DIR = '.git';
|
|
19
|
+
const GIT_SERVER_FILE = '.git_server';
|
|
20
|
+
const GIT_TOKEN_FILE = '.git_token';
|
|
21
|
+
const GIT_OWN_FILE = '.git_own';
|
|
22
|
+
const GIT_LOGIN_FILE = '.git_login';
|
|
23
|
+
const GIT_IGNORE_FILE = '.gitignore';
|
|
24
|
+
const GITHUB = 'github';
|
|
25
|
+
const GITEE = 'gitee';
|
|
26
|
+
const REPO_OWNER_USER = 'user';
|
|
27
|
+
const REPO_OWNER_ORG = 'org';
|
|
28
|
+
const VERSION_RELEASE = 'release';
|
|
29
|
+
const VERSION_DEVELOP = 'dev';
|
|
30
|
+
|
|
31
|
+
const GIT_SERVER_TYPE = [{
|
|
32
|
+
name: 'Github',
|
|
33
|
+
value: GITHUB,
|
|
34
|
+
}, {
|
|
35
|
+
name: 'Gitee',
|
|
36
|
+
value: GITEE,
|
|
37
|
+
}];
|
|
38
|
+
|
|
39
|
+
const GIT_OWNER_TYPE = [{
|
|
40
|
+
name: '个人',
|
|
41
|
+
value: REPO_OWNER_USER,
|
|
42
|
+
}, {
|
|
43
|
+
name: '组织',
|
|
44
|
+
value: REPO_OWNER_ORG,
|
|
45
|
+
}];
|
|
46
|
+
|
|
47
|
+
const GIT_OWNER_TYPE_ONLY = [{
|
|
48
|
+
name: '个人',
|
|
49
|
+
value: REPO_OWNER_USER,
|
|
50
|
+
}];
|
|
51
|
+
|
|
52
|
+
class Git {
|
|
53
|
+
constructor({ name, version, dir }, {
|
|
54
|
+
refreshServer = false,
|
|
55
|
+
refreshToken = false,
|
|
56
|
+
refreshOwner = false,
|
|
57
|
+
buildCmd = '',
|
|
58
|
+
}) {
|
|
59
|
+
this.name = name; // 项目名称
|
|
60
|
+
this.version = version; // 项目版本
|
|
61
|
+
this.dir = dir; // 源码目录
|
|
62
|
+
this.git = SimpleGit(dir); // SimpleGit实例
|
|
63
|
+
this.gitServer = null; // GitServer实例
|
|
64
|
+
this.homePath = null; // 本地缓存目录
|
|
65
|
+
this.user = null; // 用户信息
|
|
66
|
+
this.orgs = null; // 用户所属组织列表
|
|
67
|
+
this.owner = null; // 远程仓库类型
|
|
68
|
+
this.login = null; // 远程仓库登录名
|
|
69
|
+
this.repo = null; // 远程仓库信息
|
|
70
|
+
this.refreshServer = refreshServer; // 是否强制刷新远程仓库
|
|
71
|
+
this.refreshToken = refreshToken; // 是否强化刷新远程仓库token
|
|
72
|
+
this.refreshOwner = refreshOwner; // 是否强化刷新远程仓库类型
|
|
73
|
+
this.branch = null; // 本地开发分支
|
|
74
|
+
this.buildCmd = buildCmd; // 构建命令
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async prepare() {
|
|
78
|
+
this.checkHomePath(); // 检查缓存主目录
|
|
79
|
+
await this.checkGitServer(); // 检查用户远程仓库类型
|
|
80
|
+
await this.checkGitToken(); // 获取远程仓库Token
|
|
81
|
+
await this.getUserAndOrgs(); // 获取远程仓库用户和组织信息
|
|
82
|
+
await this.checkGitOwner(); // 确认远程仓库类型
|
|
83
|
+
await this.checkRepo(); // 检查并创建远程仓库
|
|
84
|
+
this.checkGitIgnore(); // 检查并创建.gitignore文件
|
|
85
|
+
await this.init(); // 完成本地仓库初始化
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async init() {
|
|
89
|
+
if (await this.getRemote()) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
await this.initAndAddRemote();
|
|
93
|
+
await this.initCommit();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async commit() {
|
|
97
|
+
// 1.生成开发分支
|
|
98
|
+
await this.getCorrectVersion();
|
|
99
|
+
// 2.检查stash区
|
|
100
|
+
await this.checkStash();
|
|
101
|
+
// 3.检查代码冲突
|
|
102
|
+
await this.checkConflicted();
|
|
103
|
+
// 4.切换开发分支
|
|
104
|
+
await this.checkoutBranch(this.branch);
|
|
105
|
+
// 5.合并远程master分支和开发分支代码
|
|
106
|
+
await this.pullRemoteMasterAndBranch();
|
|
107
|
+
// 6.将开发分支推送到远程仓库
|
|
108
|
+
await this.pushRemoteRepo(this.branch);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async publish() {
|
|
112
|
+
await this.preparePublish();
|
|
113
|
+
const cloudBuild = new CloudBuild(this, {
|
|
114
|
+
buildCmd: this.buildCmd,
|
|
115
|
+
});
|
|
116
|
+
await cloudBuild.init();
|
|
117
|
+
await cloudBuild.build();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
preparePublish() {
|
|
121
|
+
if (this.buildCmd) {
|
|
122
|
+
const buildCmdArray = this.buildCmd.split(' ');
|
|
123
|
+
if (buildCmdArray[0] !== 'npm' && buildCmdArray[0] !== 'cnpm') {
|
|
124
|
+
throw new Error('Build命令非法,必须使用npm或cnpm!');
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
this.buildCmd = 'npm run build';
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async pullRemoteMasterAndBranch() {
|
|
132
|
+
log.info(`合并 [master] -> [${this.branch}]`);
|
|
133
|
+
await this.pullRemoteRepo('master');
|
|
134
|
+
log.success('合并远程 [master] 分支代码成功');
|
|
135
|
+
await this.checkConflicted();
|
|
136
|
+
log.info('检查远程开发分支');
|
|
137
|
+
const remoteBranchList = await this.getRemoteBranchList();
|
|
138
|
+
if (remoteBranchList.indexOf(this.version) >= 0) {
|
|
139
|
+
log.info(`合并 [${this.branch}] -> [${this.branch}]`);
|
|
140
|
+
await this.pullRemoteRepo(this.branch);
|
|
141
|
+
log.success(`合并远程 [${this.branch}] 分支代码成功`);
|
|
142
|
+
await this.checkConflicted();
|
|
143
|
+
} else {
|
|
144
|
+
log.success(`不存在远程分支 [${this.branch}]`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async checkoutBranch(branch) {
|
|
149
|
+
const localBranchList = await this.git.branchLocal();
|
|
150
|
+
if (localBranchList.all.indexOf(branch) >= 0) {
|
|
151
|
+
await this.git.checkout(branch);
|
|
152
|
+
} else {
|
|
153
|
+
await this.git.checkoutLocalBranch(branch);
|
|
154
|
+
}
|
|
155
|
+
log.success(`分支切换到${branch}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async checkStash() {
|
|
159
|
+
log.info('检查stash记录');
|
|
160
|
+
const stashList = await this.git.stashList();
|
|
161
|
+
if (stashList.all.length > 0) {
|
|
162
|
+
await this.git.stash(['pop']);
|
|
163
|
+
log.success('stash pop成功');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async getCorrectVersion() {
|
|
168
|
+
// 1.获取远程分布分支
|
|
169
|
+
// 版本号规范:release/x.y.z,dev/x.y.z
|
|
170
|
+
// 版本号递增规范:major/minor/patch
|
|
171
|
+
log.info('获取代码分支');
|
|
172
|
+
const remoteBranchList = await this.getRemoteBranchList(VERSION_RELEASE);
|
|
173
|
+
let releaseVersion = null;
|
|
174
|
+
if (remoteBranchList && remoteBranchList.length > 0) {
|
|
175
|
+
releaseVersion = remoteBranchList[0];
|
|
176
|
+
}
|
|
177
|
+
log.verbose('线上最新版本号', releaseVersion);
|
|
178
|
+
// 2.生成本地开发分支
|
|
179
|
+
const devVersion = this.version;
|
|
180
|
+
if (!releaseVersion) {
|
|
181
|
+
this.branch = `${VERSION_DEVELOP}/${devVersion}`;
|
|
182
|
+
} else if (semver.gt(this.version, releaseVersion)) {
|
|
183
|
+
log.info('当前版本大于线上最新版本', `${devVersion} >= ${releaseVersion}`);
|
|
184
|
+
this.branch = `${VERSION_DEVELOP}/${devVersion}`;
|
|
185
|
+
} else {
|
|
186
|
+
log.info('当前线上版本大于本地版本', `${releaseVersion} > ${devVersion}`);
|
|
187
|
+
const incType = (await inquirer.prompt({
|
|
188
|
+
type: 'list',
|
|
189
|
+
name: 'incType',
|
|
190
|
+
message: '自动升级版本,请选择升级版本类型',
|
|
191
|
+
default: 'patch',
|
|
192
|
+
choices: [{
|
|
193
|
+
name: `小版本(${releaseVersion} -> ${semver.inc(releaseVersion, 'patch')})`,
|
|
194
|
+
value: 'patch',
|
|
195
|
+
}, {
|
|
196
|
+
name: `中版本(${releaseVersion} -> ${semver.inc(releaseVersion, 'minor')})`,
|
|
197
|
+
value: 'minor',
|
|
198
|
+
}, {
|
|
199
|
+
name: `大版本(${releaseVersion} -> ${semver.inc(releaseVersion, 'major')})`,
|
|
200
|
+
value: 'major',
|
|
201
|
+
}],
|
|
202
|
+
})).incType;
|
|
203
|
+
const incVersion = semver.inc(releaseVersion, incType);
|
|
204
|
+
this.branch = `${VERSION_DEVELOP}/${incVersion}`;
|
|
205
|
+
this.version = incVersion;
|
|
206
|
+
}
|
|
207
|
+
log.verbose('本地开发分支', this.branch);
|
|
208
|
+
// 3.将version同步到package.json
|
|
209
|
+
this.syncVersionToPackageJson();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
syncVersionToPackageJson() {
|
|
213
|
+
const pkg = fse.readJsonSync(`${this.dir}/package.json`);
|
|
214
|
+
if (pkg && pkg.version !== this.version) {
|
|
215
|
+
pkg.version = this.version;
|
|
216
|
+
fse.writeJsonSync(`${this.dir}/package.json`, pkg, { spaces: 2 });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async getRemoteBranchList(type) {
|
|
221
|
+
const remoteList = await this.git.listRemote(['--refs']);
|
|
222
|
+
let reg;
|
|
223
|
+
if (type === VERSION_RELEASE) {
|
|
224
|
+
reg = /.+?refs\/tags\/release\/(\d+\.\d+\.\d+)/g;
|
|
225
|
+
} else {
|
|
226
|
+
reg = /.+?refs\/heads\/dev\/(\d+\.\d+\.\d+)/g;
|
|
227
|
+
}
|
|
228
|
+
return remoteList.split('\n').map(remote => {
|
|
229
|
+
const match = reg.exec(remote);
|
|
230
|
+
reg.lastIndex = 0;
|
|
231
|
+
if (match && semver.valid(match[1])) {
|
|
232
|
+
return match[1];
|
|
233
|
+
}
|
|
234
|
+
}).filter(_ => _).sort((a, b) => {
|
|
235
|
+
if (semver.lte(b, a)) {
|
|
236
|
+
if (a === b) return 0;
|
|
237
|
+
return -1;
|
|
238
|
+
}
|
|
239
|
+
return 1;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async initCommit() {
|
|
244
|
+
await this.checkConflicted();
|
|
245
|
+
await this.checkNotCommitted();
|
|
246
|
+
if (await this.checkRemoteMaster()) {
|
|
247
|
+
await this.pullRemoteRepo('master', {
|
|
248
|
+
'--allow-unrelated-histories': null,
|
|
249
|
+
});
|
|
250
|
+
} else {
|
|
251
|
+
await this.pushRemoteRepo('master');
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async pullRemoteRepo(branchName, options) {
|
|
256
|
+
log.info(`同步远程${branchName}分支代码`);
|
|
257
|
+
await this.git.pull('origin', branchName, options)
|
|
258
|
+
.catch(err => {
|
|
259
|
+
log.error(err.message);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async pushRemoteRepo(branchName) {
|
|
264
|
+
log.info(`推送代码至${branchName}分支`);
|
|
265
|
+
await this.git.push('origin', branchName);
|
|
266
|
+
log.success('推送代码成功');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async checkRemoteMaster() {
|
|
270
|
+
return (await this.git.listRemote(['--refs'])).indexOf('refs/heads/master') >= 0;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async checkNotCommitted() {
|
|
274
|
+
const status = await this.git.status();
|
|
275
|
+
if (status.not_added.length > 0 ||
|
|
276
|
+
status.created.length > 0 ||
|
|
277
|
+
status.deleted.length > 0 ||
|
|
278
|
+
status.modified.length > 0 ||
|
|
279
|
+
status.renamed.length > 0
|
|
280
|
+
) {
|
|
281
|
+
log.verbose('status', status);
|
|
282
|
+
await this.git.add(status.not_added);
|
|
283
|
+
await this.git.add(status.created);
|
|
284
|
+
await this.git.add(status.deleted);
|
|
285
|
+
await this.git.add(status.modified);
|
|
286
|
+
await this.git.add(status.renamed);
|
|
287
|
+
let message;
|
|
288
|
+
while (!message) {
|
|
289
|
+
message = (await inquirer.prompt({
|
|
290
|
+
type: 'text',
|
|
291
|
+
name: 'message',
|
|
292
|
+
message: '请输入commit信息:',
|
|
293
|
+
})).message;
|
|
294
|
+
}
|
|
295
|
+
await this.git.commit(message);
|
|
296
|
+
log.success('本次commit提交成功');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async checkConflicted() {
|
|
301
|
+
log.info('代码冲突检查');
|
|
302
|
+
const status = await this.git.status();
|
|
303
|
+
if (status.conflicted.length > 0) {
|
|
304
|
+
throw new Error('当前代码存在冲突,请手动处理合并后再试!');
|
|
305
|
+
}
|
|
306
|
+
log.success('代码冲突检查通过');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
getRemote() {
|
|
310
|
+
const gitPath = path.resolve(this.dir, GIT_ROOT_DIR);
|
|
311
|
+
this.remote = this.gitServer.getRemote(this.login, this.name);
|
|
312
|
+
if (fs.existsSync(gitPath)) {
|
|
313
|
+
log.success('git已完成初始化');
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async initAndAddRemote() {
|
|
319
|
+
log.info('执行git初始化');
|
|
320
|
+
await this.git.init(this.dir);
|
|
321
|
+
log.info('添加git remote');
|
|
322
|
+
const remotes = await this.git.getRemotes();
|
|
323
|
+
log.verbose('git remotes', remotes);
|
|
324
|
+
if (!remotes.find(item => item.name === 'origin')) {
|
|
325
|
+
await this.git.addRemote('origin', this.remote);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
checkHomePath() {
|
|
330
|
+
if (!this.homePath) {
|
|
331
|
+
if (process.env.CLI_HOME_PATH) {
|
|
332
|
+
this.homePath = process.env.CLI_HOME_PATH;
|
|
333
|
+
} else {
|
|
334
|
+
this.homePath = path.resolve(userHome, DEFAULT_CLI_HOME);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
log.verbose('home', this.homePath);
|
|
338
|
+
fse.ensureDirSync(this.homePath);
|
|
339
|
+
if (!fs.existsSync(this.homePath)) {
|
|
340
|
+
throw new Error('用户主目录获取失败!');
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async checkGitServer() {
|
|
345
|
+
const gitServerPath = this.createPath(GIT_SERVER_FILE);
|
|
346
|
+
let gitServer = readFile(gitServerPath);
|
|
347
|
+
if (!gitServer || this.refreshServer) {
|
|
348
|
+
gitServer = (await inquirer.prompt({
|
|
349
|
+
type: 'list',
|
|
350
|
+
name: 'gitServer',
|
|
351
|
+
message: '请选择您想要托管的Git平台',
|
|
352
|
+
default: GITHUB,
|
|
353
|
+
choices: GIT_SERVER_TYPE,
|
|
354
|
+
})).gitServer;
|
|
355
|
+
writeFile(gitServerPath, gitServer);
|
|
356
|
+
log.success('git server写入成功', `${gitServer} -> ${gitServerPath}`);
|
|
357
|
+
} else {
|
|
358
|
+
log.success('git server获取成功', gitServer);
|
|
359
|
+
}
|
|
360
|
+
this.gitServer = this.createGitServer(gitServer);
|
|
361
|
+
if (!this.gitServer) {
|
|
362
|
+
throw new Error('GitServer初始化失败!');
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async checkGitToken() {
|
|
367
|
+
const tokenPath = this.createPath(GIT_TOKEN_FILE);
|
|
368
|
+
let token = readFile(tokenPath);
|
|
369
|
+
if (!token || this.refreshToken) {
|
|
370
|
+
log.warn(this.gitServer.type + ' token未生成', '请先生成' + this.gitServer.type + ' token,' + terminalLink('链接', this.gitServer.getTokenUrl()));
|
|
371
|
+
token = (await inquirer.prompt({
|
|
372
|
+
type: 'password',
|
|
373
|
+
name: 'token',
|
|
374
|
+
message: '请将token复制到这里',
|
|
375
|
+
default: '',
|
|
376
|
+
})).token;
|
|
377
|
+
writeFile(tokenPath, token);
|
|
378
|
+
log.success('token写入成功', `${token} -> ${tokenPath}`);
|
|
379
|
+
} else {
|
|
380
|
+
log.success('token获取成功', tokenPath);
|
|
381
|
+
}
|
|
382
|
+
this.token = token;
|
|
383
|
+
this.gitServer.setToken(token);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async getUserAndOrgs() {
|
|
387
|
+
this.user = await this.gitServer.getUser();
|
|
388
|
+
if (!this.user) {
|
|
389
|
+
throw new Error('用户信息获取失败!');
|
|
390
|
+
}
|
|
391
|
+
log.verbose('user', this.user);
|
|
392
|
+
this.orgs = await this.gitServer.getOrg(this.user.login);
|
|
393
|
+
if (!this.orgs) {
|
|
394
|
+
throw new Error('组织信息获取失败!');
|
|
395
|
+
}
|
|
396
|
+
log.verbose('orgs', this.orgs);
|
|
397
|
+
log.success(this.gitServer.type + ' 用户和组织信息获取成功');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async checkGitOwner() {
|
|
401
|
+
const ownerPath = this.createPath(GIT_OWN_FILE);
|
|
402
|
+
const loginPath = this.createPath(GIT_LOGIN_FILE);
|
|
403
|
+
let owner = readFile(ownerPath);
|
|
404
|
+
let login = readFile(loginPath);
|
|
405
|
+
if (!owner || !login || this.refreshOwner) {
|
|
406
|
+
owner = (await inquirer.prompt({
|
|
407
|
+
type: 'list',
|
|
408
|
+
name: 'owner',
|
|
409
|
+
message: '请选择远程仓库类型',
|
|
410
|
+
default: REPO_OWNER_USER,
|
|
411
|
+
choices: this.orgs.length > 0 ? GIT_OWNER_TYPE : GIT_OWNER_TYPE_ONLY,
|
|
412
|
+
})).owner;
|
|
413
|
+
if (owner === REPO_OWNER_USER) {
|
|
414
|
+
login = this.user.login;
|
|
415
|
+
} else {
|
|
416
|
+
login = (await inquirer.prompt({
|
|
417
|
+
type: 'list',
|
|
418
|
+
name: 'login',
|
|
419
|
+
message: '请选择',
|
|
420
|
+
choices: this.orgs.map(item => ({
|
|
421
|
+
name: item.login,
|
|
422
|
+
value: item.login,
|
|
423
|
+
})),
|
|
424
|
+
})).login;
|
|
425
|
+
}
|
|
426
|
+
writeFile(ownerPath, owner);
|
|
427
|
+
writeFile(loginPath, login);
|
|
428
|
+
log.success('owner写入成功', `${owner} -> ${ownerPath}`);
|
|
429
|
+
log.success('login写入成功', `${login} -> ${loginPath}`);
|
|
430
|
+
} else {
|
|
431
|
+
log.success('owner获取成功', owner);
|
|
432
|
+
log.success('login获取成功', login);
|
|
433
|
+
}
|
|
434
|
+
this.owner = owner;
|
|
435
|
+
this.login = login;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async checkRepo() {
|
|
439
|
+
let repo = await this.gitServer.getRepo(this.login, this.name);
|
|
440
|
+
if (!repo) {
|
|
441
|
+
let spinner = spinnerStart('开始创建远程仓库...');
|
|
442
|
+
try {
|
|
443
|
+
if (this.owner === REPO_OWNER_USER) {
|
|
444
|
+
repo = await this.gitServer.createRepo(this.name);
|
|
445
|
+
} else {
|
|
446
|
+
this.gitServer.createOrgRepo(this.name, this.login);
|
|
447
|
+
}
|
|
448
|
+
} catch (e) {
|
|
449
|
+
log.error(e);
|
|
450
|
+
} finally {
|
|
451
|
+
spinner.stop(true);
|
|
452
|
+
}
|
|
453
|
+
if (repo) {
|
|
454
|
+
log.success('远程仓库创建成功');
|
|
455
|
+
} else {
|
|
456
|
+
throw new Error('远程仓库创建失败');
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
log.success('远程仓库信息获取成功');
|
|
460
|
+
}
|
|
461
|
+
log.verbose('repo', repo);
|
|
462
|
+
this.repo = repo;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
checkGitIgnore() {
|
|
466
|
+
const gitIgnore = path.resolve(this.dir, GIT_IGNORE_FILE);
|
|
467
|
+
if (!fs.existsSync(gitIgnore)) {
|
|
468
|
+
writeFile(gitIgnore, `.DS_Store
|
|
469
|
+
node_modules
|
|
470
|
+
/dist
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
# local env files
|
|
474
|
+
.env.local
|
|
475
|
+
.env.*.local
|
|
476
|
+
|
|
477
|
+
# Log files
|
|
478
|
+
npm-debug.log*
|
|
479
|
+
yarn-debug.log*
|
|
480
|
+
yarn-error.log*
|
|
481
|
+
pnpm-debug.log*
|
|
482
|
+
|
|
483
|
+
# Editor directories and files
|
|
484
|
+
.idea
|
|
485
|
+
.vscode
|
|
486
|
+
*.suo
|
|
487
|
+
*.ntvs*
|
|
488
|
+
*.njsproj
|
|
489
|
+
*.sln
|
|
490
|
+
*.sw?`);
|
|
491
|
+
log.success(`自动写入${GIT_IGNORE_FILE}文件成功`);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
createGitServer(gitServer = '') {
|
|
496
|
+
if (gitServer === GITHUB) {
|
|
497
|
+
return new Github();
|
|
498
|
+
} else if (gitServer === GITEE) {
|
|
499
|
+
return new Gitee();
|
|
500
|
+
}
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
createPath(file) {
|
|
505
|
+
const rootDir = path.resolve(this.homePath, GIT_ROOT_DIR);
|
|
506
|
+
const filePath = path.resolve(rootDir, file);
|
|
507
|
+
fse.ensureDirSync(rootDir);
|
|
508
|
+
return filePath;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
module.exports = Git;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@imooc-cli-dev-gd/git",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "> TODO: description",
|
|
5
|
+
"author": "sam <247765564@qq.com>",
|
|
6
|
+
"homepage": "",
|
|
7
|
+
"license": "ISC",
|
|
8
|
+
"main": "lib/index.js",
|
|
9
|
+
"directories": {
|
|
10
|
+
"lib": "lib",
|
|
11
|
+
"test": "__tests__"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"lib"
|
|
15
|
+
],
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://git.imooc.com/class-110/imooc-cli-dev.git"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@imooc-cli-dev-gd/log": "file:../../utils/log",
|
|
25
|
+
"@imooc-cli-dev-gd/utils": "file:../../utils/utils",
|
|
26
|
+
"@imooc-cli-dev-gd/cloudbuild": "file:../../models/cloudbuild",
|
|
27
|
+
"axios": "^0.21.1",
|
|
28
|
+
"fs-extra": "^10.0.0",
|
|
29
|
+
"inquirer": "^8.1.0",
|
|
30
|
+
"semver": "^7.3.5",
|
|
31
|
+
"simple-git": "^2.39.0",
|
|
32
|
+
"terminal-link": "^2.1.1",
|
|
33
|
+
"user-home": "^3.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fse = require('fs-extra');
|
|
5
|
+
const pkgDir = require('pkg-dir').sync;
|
|
6
|
+
const pathExists = require('path-exists').sync;
|
|
7
|
+
const npminstall = require('npminstall');
|
|
8
|
+
const { isObject } = require('@imooc-cli-dev-gd/utils');
|
|
9
|
+
const formatPath = require('@imooc-cli-dev-gd/format-path');
|
|
10
|
+
const { getDefaultRegistry, getNpmLatestVersion } = require('@imooc-cli-dev-gd/get-npm-info');
|
|
11
|
+
|
|
12
|
+
class Package {
|
|
13
|
+
constructor(options) {
|
|
14
|
+
if (!options) {
|
|
15
|
+
throw new Error('Package类的options参数不能为空!');
|
|
16
|
+
}
|
|
17
|
+
if (!isObject(options)) {
|
|
18
|
+
throw new Error('Package类的options参数必须为对象!');
|
|
19
|
+
}
|
|
20
|
+
// package的目标路径
|
|
21
|
+
this.targetPath = options.targetPath;
|
|
22
|
+
// 缓存package的路径
|
|
23
|
+
this.storeDir = options.storeDir;
|
|
24
|
+
// package的name
|
|
25
|
+
this.packageName = options.packageName;
|
|
26
|
+
// package的version
|
|
27
|
+
this.packageVersion = options.packageVersion;
|
|
28
|
+
// package的缓存目录前缀
|
|
29
|
+
this.cacheFilePathPrefix = this.packageName.replace('/', '_');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async prepare() {
|
|
33
|
+
if (this.storeDir && !pathExists(this.storeDir)) {
|
|
34
|
+
fse.mkdirpSync(this.storeDir);
|
|
35
|
+
}
|
|
36
|
+
if (this.packageVersion === 'latest') {
|
|
37
|
+
this.packageVersion = await getNpmLatestVersion(this.packageName);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get cacheFilePath() {
|
|
42
|
+
return path.resolve(this.storeDir, `_${this.cacheFilePathPrefix}@${this.packageVersion}@${this.packageName}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getSpecificCacheFilePath(packageVersion) {
|
|
46
|
+
return path.resolve(this.storeDir, `_${this.cacheFilePathPrefix}@${packageVersion}@${this.packageName}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 判断当前Package是否存在
|
|
50
|
+
async exists() {
|
|
51
|
+
if (this.storeDir) {
|
|
52
|
+
await this.prepare();
|
|
53
|
+
return pathExists(this.cacheFilePath);
|
|
54
|
+
} else {
|
|
55
|
+
return pathExists(this.targetPath);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 安装Package
|
|
60
|
+
async install() {
|
|
61
|
+
await this.prepare();
|
|
62
|
+
return npminstall({
|
|
63
|
+
root: this.targetPath,
|
|
64
|
+
storeDir: this.storeDir,
|
|
65
|
+
registry: getDefaultRegistry(),
|
|
66
|
+
pkgs: [{
|
|
67
|
+
name: this.packageName,
|
|
68
|
+
version: this.packageVersion,
|
|
69
|
+
}],
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 更新Package
|
|
74
|
+
async update() {
|
|
75
|
+
await this.prepare();
|
|
76
|
+
// 1. 获取最新的npm模块版本号
|
|
77
|
+
const latestPackageVersion = await getNpmLatestVersion(this.packageName);
|
|
78
|
+
// 2. 查询最新版本号对应的路径是否存在
|
|
79
|
+
const latestFilePath = this.getSpecificCacheFilePath(latestPackageVersion);
|
|
80
|
+
// 3. 如果不存在,则直接安装最新版本
|
|
81
|
+
if (!pathExists(latestFilePath)) {
|
|
82
|
+
await npminstall({
|
|
83
|
+
root: this.targetPath,
|
|
84
|
+
storeDir: this.storeDir,
|
|
85
|
+
registry: getDefaultRegistry(),
|
|
86
|
+
pkgs: [{
|
|
87
|
+
name: this.packageName,
|
|
88
|
+
version: latestPackageVersion,
|
|
89
|
+
}],
|
|
90
|
+
});
|
|
91
|
+
this.packageVersion = latestPackageVersion;
|
|
92
|
+
} else {
|
|
93
|
+
this.packageVersion = latestPackageVersion;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 获取入口文件的路径
|
|
98
|
+
getRootFilePath() {
|
|
99
|
+
function _getRootFile(targetPath) {
|
|
100
|
+
// 1. 获取package.json所在目录
|
|
101
|
+
const dir = pkgDir(targetPath);
|
|
102
|
+
if (dir) {
|
|
103
|
+
// 2. 读取package.json
|
|
104
|
+
const pkgFile = require(path.resolve(dir, 'package.json'));
|
|
105
|
+
// 3. 寻找main/lib
|
|
106
|
+
if (pkgFile && pkgFile.main) {
|
|
107
|
+
// 4. 路径的兼容(macOS/windows)
|
|
108
|
+
return formatPath(path.resolve(dir, pkgFile.main));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (this.storeDir) {
|
|
114
|
+
return _getRootFile(this.cacheFilePath);
|
|
115
|
+
} else {
|
|
116
|
+
return _getRootFile(this.targetPath);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = Package;
|