@clickview/ship-it 0.0.5 → 0.0.6-rc.1
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/README.md +32 -1
- package/lib/src/api-clients/git-lab/GitLabApiClient.js +14 -5
- package/lib/src/commands/StatusCommand.js +19 -3
- package/lib/src/constants/GitLabMemberIds.js +6 -0
- package/lib/src/tasks/ValidateCommitsTask.js +14 -8
- package/lib/src/tasks/ValidateTask.js +125 -63
- package/lib/src/utils/GroupIds.js +6 -0
- package/lib/src/utils/ProjectIds.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1 +1,32 @@
|
|
|
1
|
-
|
|
1
|
+
# Ship It
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
* NodeJS (and npm)
|
|
6
|
+
* Access to the ClickView organization on [npm](https://www.npmjs.com). You will need to add an npm auth token to a global or local `.npmrc` file
|
|
7
|
+
* Add the `Ship It` user as a `Developer` to each GitLab project you would like to manage using the Ship It CLI
|
|
8
|
+
|
|
9
|
+
## Getting Started
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
git clone https://gitlab.cvinternal.net/front-end/ship-it.git
|
|
13
|
+
cd ship-it
|
|
14
|
+
npm install
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
After installation, you can run `npm run start -- help` for a list of Shit Commands.
|
|
18
|
+
|
|
19
|
+
## Commands
|
|
20
|
+
|
|
21
|
+
The available commands are listed below. If you run into issues check that you have met all the [prerequisites](#prerequisites) first.
|
|
22
|
+
|
|
23
|
+
### Status
|
|
24
|
+
|
|
25
|
+
* Command: `npm run start -- status -p [projectId] -m [milestone]`
|
|
26
|
+
* Example: `npm run start -- status -p 887 -m T3`
|
|
27
|
+
|
|
28
|
+
Get the status of all MRs in a milestone. You can run this command at any time to check whether a project's miletone is ready for release.
|
|
29
|
+
|
|
30
|
+
You can find your numeric project id from your project page on GitLab.
|
|
31
|
+
|
|
32
|
+
|
|
@@ -55,6 +55,8 @@ var node_1 = require("@gitbeaker/node");
|
|
|
55
55
|
var types_1 = require("../../types");
|
|
56
56
|
var errors_1 = require("../../errors");
|
|
57
57
|
var GitLabMapper_1 = require("./GitLabMapper");
|
|
58
|
+
var GroupIds_1 = require("../../utils/GroupIds");
|
|
59
|
+
var GitLabMemberIds_1 = require("../../constants/GitLabMemberIds");
|
|
58
60
|
var GitLabApiClient = /** @class */ (function () {
|
|
59
61
|
function GitLabApiClient(config) {
|
|
60
62
|
this.config = config;
|
|
@@ -306,7 +308,8 @@ var GitLabApiClient = /** @class */ (function () {
|
|
|
306
308
|
switch (_a.label) {
|
|
307
309
|
case 0: return [4 /*yield*/, this.client.MergeRequests.create(projectId, sourceBranch, targetBranch, title, {
|
|
308
310
|
description: description,
|
|
309
|
-
milestoneId: milestone.id
|
|
311
|
+
milestoneId: milestone.id,
|
|
312
|
+
assigneeId: GitLabMemberIds_1.GitLabMemberIds.SHIP_IT
|
|
310
313
|
})];
|
|
311
314
|
case 1:
|
|
312
315
|
mergeRequest = _a.sent();
|
|
@@ -364,13 +367,19 @@ var GitLabApiClient = /** @class */ (function () {
|
|
|
364
367
|
};
|
|
365
368
|
GitLabApiClient.prototype.getMilestone = function (projectId, milestoneName) {
|
|
366
369
|
return __awaiter(this, void 0, void 0, function () {
|
|
367
|
-
var
|
|
370
|
+
var groupMilestones, milestone, projectMilestones;
|
|
368
371
|
return __generator(this, function (_a) {
|
|
369
372
|
switch (_a.label) {
|
|
370
|
-
case 0: return [4 /*yield*/, this.client.
|
|
373
|
+
case 0: return [4 /*yield*/, this.client.GroupMilestones.all(GroupIds_1.GroupIds.CLICKVIEW)];
|
|
371
374
|
case 1:
|
|
372
|
-
|
|
373
|
-
milestone =
|
|
375
|
+
groupMilestones = _a.sent();
|
|
376
|
+
milestone = groupMilestones.find(function (milestone) { return milestone.title === milestoneName; });
|
|
377
|
+
if (milestone)
|
|
378
|
+
return [2 /*return*/, GitLabMapper_1.GitLabMapper.milestone(milestone)];
|
|
379
|
+
return [4 /*yield*/, this.client.ProjectMilestones.all(projectId)];
|
|
380
|
+
case 2:
|
|
381
|
+
projectMilestones = _a.sent();
|
|
382
|
+
milestone = projectMilestones.find(function (milestone) { return milestone.title === milestoneName; });
|
|
374
383
|
if (!milestone)
|
|
375
384
|
return [2 /*return*/, null];
|
|
376
385
|
return [2 /*return*/, GitLabMapper_1.GitLabMapper.milestone(milestone)];
|
|
@@ -53,15 +53,29 @@ require("reflect-metadata");
|
|
|
53
53
|
var inversify_1 = require("inversify");
|
|
54
54
|
var types_1 = require("../types");
|
|
55
55
|
var StatusCommand = /** @class */ (function () {
|
|
56
|
-
function StatusCommand(validateTask) {
|
|
56
|
+
function StatusCommand(validateTask, validateDescriptionsTask, validateCommitsTask) {
|
|
57
57
|
this.validateTask = validateTask;
|
|
58
|
+
this.validateDescriptionsTask = validateDescriptionsTask;
|
|
59
|
+
this.validateCommitsTask = validateCommitsTask;
|
|
58
60
|
}
|
|
59
61
|
StatusCommand.prototype.run = function (options) {
|
|
60
62
|
return __awaiter(this, void 0, void 0, function () {
|
|
63
|
+
var cont;
|
|
61
64
|
return __generator(this, function (_a) {
|
|
62
65
|
switch (_a.label) {
|
|
63
66
|
case 0: return [4 /*yield*/, this.validateTask.run(options)];
|
|
64
|
-
case 1:
|
|
67
|
+
case 1:
|
|
68
|
+
cont = _a.sent();
|
|
69
|
+
// Check if we want to continue with inquirer?
|
|
70
|
+
if (!cont)
|
|
71
|
+
return [2 /*return*/, false];
|
|
72
|
+
return [4 /*yield*/, this.validateDescriptionsTask.run(options)];
|
|
73
|
+
case 2:
|
|
74
|
+
cont = _a.sent();
|
|
75
|
+
if (!cont)
|
|
76
|
+
return [2 /*return*/, false];
|
|
77
|
+
return [4 /*yield*/, this.validateCommitsTask.run(options)];
|
|
78
|
+
case 3: return [2 /*return*/, _a.sent()];
|
|
65
79
|
}
|
|
66
80
|
});
|
|
67
81
|
});
|
|
@@ -69,7 +83,9 @@ var StatusCommand = /** @class */ (function () {
|
|
|
69
83
|
StatusCommand = __decorate([
|
|
70
84
|
(0, inversify_1.injectable)(),
|
|
71
85
|
__param(0, (0, inversify_1.inject)(types_1.TASKS.ValidateTask)),
|
|
72
|
-
|
|
86
|
+
__param(1, (0, inversify_1.inject)(types_1.TASKS.ValidateDescriptionsTask)),
|
|
87
|
+
__param(2, (0, inversify_1.inject)(types_1.TASKS.ValidateCommitsTask)),
|
|
88
|
+
__metadata("design:paramtypes", [Object, Object, Object])
|
|
73
89
|
], StatusCommand);
|
|
74
90
|
return StatusCommand;
|
|
75
91
|
}());
|
|
@@ -60,32 +60,38 @@ var ValidateCommitsTask = /** @class */ (function () {
|
|
|
60
60
|
}
|
|
61
61
|
ValidateCommitsTask.prototype.run = function (options) {
|
|
62
62
|
return __awaiter(this, void 0, void 0, function () {
|
|
63
|
-
var projects, cont, _i, projects_1, project, tag, devCommits, shouldCherryPick;
|
|
64
|
-
return __generator(this, function (
|
|
65
|
-
switch (
|
|
63
|
+
var projects, cont, _i, projects_1, project, _a, branch, tag, devCommits, shouldCherryPick;
|
|
64
|
+
return __generator(this, function (_b) {
|
|
65
|
+
switch (_b.label) {
|
|
66
66
|
case 0:
|
|
67
67
|
utils_1.Logger.logTask('Validating dev commits');
|
|
68
68
|
return [4 /*yield*/, this.service.getProjects(options)];
|
|
69
69
|
case 1:
|
|
70
|
-
projects =
|
|
70
|
+
projects = _b.sent();
|
|
71
71
|
cont = true;
|
|
72
72
|
_i = 0, projects_1 = projects;
|
|
73
|
-
|
|
73
|
+
_b.label = 2;
|
|
74
74
|
case 2:
|
|
75
75
|
if (!(_i < projects_1.length)) return [3 /*break*/, 6];
|
|
76
76
|
project = projects_1[_i];
|
|
77
77
|
return [4 /*yield*/, this.service.getNextBranchInfo(project.id, options.version)];
|
|
78
78
|
case 3:
|
|
79
|
-
|
|
79
|
+
_a = _b.sent(), branch = _a.branch, tag = _a.tag;
|
|
80
|
+
/**
|
|
81
|
+
* If the release branch already exists, this can be skipped as this
|
|
82
|
+
* validation is only required when creating a new release
|
|
83
|
+
*/
|
|
84
|
+
if (branch)
|
|
85
|
+
return [3 /*break*/, 5];
|
|
80
86
|
return [4 /*yield*/, this.service.getDevCommitsSince(project.id, tag.target)];
|
|
81
87
|
case 4:
|
|
82
|
-
devCommits =
|
|
88
|
+
devCommits = _b.sent();
|
|
83
89
|
shouldCherryPick = utils_1.Commits.shouldCherryPick(project, devCommits);
|
|
84
90
|
if (shouldCherryPick) {
|
|
85
91
|
utils_1.Logger.logError("[".concat(project.title, "]: Requires cherrypicking"));
|
|
86
92
|
cont = false;
|
|
87
93
|
}
|
|
88
|
-
|
|
94
|
+
_b.label = 5;
|
|
89
95
|
case 5:
|
|
90
96
|
_i++;
|
|
91
97
|
return [3 /*break*/, 2];
|
|
@@ -75,7 +75,7 @@ var ValidateTask = /** @class */ (function () {
|
|
|
75
75
|
}
|
|
76
76
|
ValidateTask.prototype.run = function (options) {
|
|
77
77
|
return __awaiter(this, void 0, void 0, function () {
|
|
78
|
-
var projects, cont, _loop_1, _i, projects_1, project;
|
|
78
|
+
var projects, cont, _loop_1, this_1, _i, projects_1, project;
|
|
79
79
|
return __generator(this, function (_a) {
|
|
80
80
|
switch (_a.label) {
|
|
81
81
|
case 0:
|
|
@@ -86,75 +86,137 @@ var ValidateTask = /** @class */ (function () {
|
|
|
86
86
|
utils_1.Logger.logMessage("Found ".concat(projects.length, " projects that need to be released"));
|
|
87
87
|
cont = true;
|
|
88
88
|
_loop_1 = function (project) {
|
|
89
|
-
var groupedMRs
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
89
|
+
var groupedMRs, count, ready, wrongTargetMrs, message;
|
|
90
|
+
return __generator(this, function (_b) {
|
|
91
|
+
switch (_b.label) {
|
|
92
|
+
case 0:
|
|
93
|
+
groupedMRs = lodash_1.default.groupBy(project.mergeRequests, function (mr) { return mr.status; });
|
|
94
|
+
count = Object.keys(groupedMRs).reduce(function (prev, current) {
|
|
95
|
+
var _a;
|
|
96
|
+
return __assign(__assign({}, prev), (_a = {}, _a[current] = groupedMRs[current].length, _a));
|
|
97
|
+
}, {});
|
|
98
|
+
ready = !count.opened || count.opened === 0;
|
|
99
|
+
return [4 /*yield*/, this_1.getMRsTargetingWrongBranch(project, options.version)];
|
|
100
|
+
case 1:
|
|
101
|
+
wrongTargetMrs = _b.sent();
|
|
102
|
+
if (wrongTargetMrs.length)
|
|
103
|
+
ready = false;
|
|
104
|
+
if (!ready)
|
|
105
|
+
cont = false;
|
|
106
|
+
if (ready) {
|
|
107
|
+
utils_1.Logger.logSuccess("[".concat(project.title, "] READY"));
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
message = "[".concat(project.title, "] NOT READY");
|
|
111
|
+
if (count.opened)
|
|
112
|
+
message += " Contains ".concat(count.opened, " Open MR(s)");
|
|
113
|
+
if (wrongTargetMrs)
|
|
114
|
+
message += " Contains ".concat(wrongTargetMrs.length, " MR(s) not targeting dev or release branch");
|
|
115
|
+
utils_1.Logger.logError(message);
|
|
116
|
+
}
|
|
117
|
+
return [2 /*return*/];
|
|
118
|
+
}
|
|
119
|
+
});
|
|
114
120
|
};
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (!
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
this_1 = this;
|
|
122
|
+
_i = 0, projects_1 = projects;
|
|
123
|
+
_a.label = 2;
|
|
124
|
+
case 2:
|
|
125
|
+
if (!(_i < projects_1.length)) return [3 /*break*/, 5];
|
|
126
|
+
project = projects_1[_i];
|
|
127
|
+
return [5 /*yield**/, _loop_1(project)];
|
|
128
|
+
case 3:
|
|
129
|
+
_a.sent();
|
|
130
|
+
_a.label = 4;
|
|
131
|
+
case 4:
|
|
132
|
+
_i++;
|
|
133
|
+
return [3 /*break*/, 2];
|
|
134
|
+
case 5:
|
|
135
|
+
if (!!cont) return [3 /*break*/, 7];
|
|
136
|
+
return [4 /*yield*/, this.logNotReady(projects, options.version)];
|
|
137
|
+
case 6:
|
|
138
|
+
_a.sent();
|
|
139
|
+
return [2 /*return*/, false];
|
|
140
|
+
case 7:
|
|
123
141
|
utils_1.Logger.logSuccess('\nRelease is ready to go out');
|
|
124
142
|
return [2 /*return*/, true];
|
|
125
143
|
}
|
|
126
144
|
});
|
|
127
145
|
});
|
|
128
146
|
};
|
|
129
|
-
ValidateTask.prototype.logNotReady = function (projects) {
|
|
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
|
-
|
|
147
|
+
ValidateTask.prototype.logNotReady = function (projects, version) {
|
|
148
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
149
|
+
var allNotReadyMrs, allWrongTargetMrs, _i, projects_2, project, notReadyMrs, wrongTargetMrs, groupedByUser, key, name, _a, _b, mr;
|
|
150
|
+
return __generator(this, function (_c) {
|
|
151
|
+
switch (_c.label) {
|
|
152
|
+
case 0:
|
|
153
|
+
utils_1.Logger.logError("\nThis release is NOT READY to go out, here are the outstanding MRs: \n");
|
|
154
|
+
allNotReadyMrs = [];
|
|
155
|
+
allWrongTargetMrs = [];
|
|
156
|
+
_i = 0, projects_2 = projects;
|
|
157
|
+
_c.label = 1;
|
|
158
|
+
case 1:
|
|
159
|
+
if (!(_i < projects_2.length)) return [3 /*break*/, 4];
|
|
160
|
+
project = projects_2[_i];
|
|
161
|
+
notReadyMrs = project.mergeRequests.filter(function (mr) { return mr.status === 'opened'; });
|
|
162
|
+
return [4 /*yield*/, this.getMRsTargetingWrongBranch(project, version)];
|
|
163
|
+
case 2:
|
|
164
|
+
wrongTargetMrs = _c.sent();
|
|
165
|
+
if (notReadyMrs.length)
|
|
166
|
+
allNotReadyMrs.push.apply(allNotReadyMrs, notReadyMrs);
|
|
167
|
+
if (wrongTargetMrs.length)
|
|
168
|
+
allWrongTargetMrs.push.apply(allWrongTargetMrs, wrongTargetMrs);
|
|
169
|
+
_c.label = 3;
|
|
170
|
+
case 3:
|
|
171
|
+
_i++;
|
|
172
|
+
return [3 /*break*/, 1];
|
|
173
|
+
case 4:
|
|
174
|
+
groupedByUser = lodash_1.default.groupBy(allNotReadyMrs, function (mr) { return mr.assignee; });
|
|
175
|
+
for (key in groupedByUser) {
|
|
176
|
+
name = key;
|
|
177
|
+
if (!name || name === 'undefined')
|
|
178
|
+
name = 'No Assignee';
|
|
179
|
+
utils_1.Logger.logMessage(name);
|
|
180
|
+
for (_a = 0, _b = groupedByUser[key]; _a < _b.length; _a++) {
|
|
181
|
+
mr = _b[_a];
|
|
182
|
+
utils_1.Logger.logMessage("- ".concat(mr.url));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (allWrongTargetMrs.length) {
|
|
186
|
+
utils_1.Logger.logMessage('\nMRs that are not targeting dev or release branch\n');
|
|
187
|
+
allWrongTargetMrs.forEach(function (mr) { return utils_1.Logger.logError("- ".concat(mr.url)); });
|
|
188
|
+
}
|
|
189
|
+
utils_1.Logger.logMessage('\n');
|
|
190
|
+
return [2 /*return*/];
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Finds any MRs with the milestone that are either not targeting dev
|
|
197
|
+
* or the release branch if one is already made
|
|
198
|
+
*/
|
|
199
|
+
ValidateTask.prototype.getMRsTargetingWrongBranch = function (project, version) {
|
|
200
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
201
|
+
var nonDevMrs, _a, premadeRelease, releaseName, nonDevAndReleaseMrs;
|
|
202
|
+
return __generator(this, function (_b) {
|
|
203
|
+
switch (_b.label) {
|
|
204
|
+
case 0:
|
|
205
|
+
nonDevMrs = project.mergeRequests.filter(function (mr) {
|
|
206
|
+
return mr.targetBranch !== 'dev';
|
|
207
|
+
});
|
|
208
|
+
if (!nonDevMrs.length)
|
|
209
|
+
return [2 /*return*/, []];
|
|
210
|
+
return [4 /*yield*/, this.service.getNextBranchInfo(project.id, version)];
|
|
211
|
+
case 1:
|
|
212
|
+
_a = _b.sent(), premadeRelease = _a.branch, releaseName = _a.branchName;
|
|
213
|
+
if (!premadeRelease)
|
|
214
|
+
return [2 /*return*/, nonDevMrs];
|
|
215
|
+
nonDevAndReleaseMrs = nonDevMrs.filter(function (mr) { return mr.targetBranch !== releaseName; });
|
|
216
|
+
return [2 /*return*/, nonDevAndReleaseMrs];
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
});
|
|
158
220
|
};
|
|
159
221
|
ValidateTask = __decorate([
|
|
160
222
|
(0, inversify_1.injectable)(),
|