Skip to content

Commit b95792e

Browse files
committed
Add support for multiple projects and/or groups
1 parent 103e919 commit b95792e

File tree

7 files changed

+90
-49
lines changed

7 files changed

+90
-49
lines changed

gtt-report.js

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ program
5151
.option('--merge_request_columns <columns>', 'include the given columns in the merge request part of the report', collect, null)
5252
.option('--user_columns', 'include user columns in the report')
5353
.option('--quiet', 'only output report')
54+
.option('--verbose', 'show verbose output')
5455
.option('--show_without_times', 'show issues/merge requests without time records')
5556
.option('-p --proxy <proxy>', 'use a proxy server with the given url')
5657
.parse(process.argv);
@@ -88,13 +89,26 @@ config
8889
.set('userColumns', program.user_columns)
8990
.set('proxy', program.proxy)
9091
.set('type', program.type)
91-
.set('subgroups', program.subgroups);
92+
.set('subgroups', program.subgroups)
93+
.set('_verbose', program.verbose);
9294

9395
Cli.quiet = config.get('quiet');
96+
Cli.verbose = config.get('_verbose');
97+
98+
99+
// create stuff
100+
let reports = new ReportCollection(config),
101+
master = new Report(config),
102+
projectLabels = _.isArray(config.get('project')) ? config.get('project').join('", "') : config.get('project'),
103+
projects = _.isArray(config.get('project')) ? config.get('project') : [config.get('project')],
104+
output;
94105

95106
// warnings
96107
if (config.get('iids').length > 1 && config.get('query').length > 1) {
97-
Cli.warn(`The ids argument is ignored when querying multiple data types`);
108+
Cli.warn(`The ids argument is ignored when querying issues and merge requests`);
109+
}
110+
if (config.get('iids').length > 1 && (config.get('type') !== 'project' || projects.length > 1)) {
111+
Cli.warn(`The ids argument is ignored when querying multiple projects`);
98112
}
99113
if ((config.get('report').includes('issues') && !config.get('query').includes('issues'))) {
100114
Cli.warn(`Issues are included in the report but not queried.`);
@@ -115,11 +129,6 @@ if (!config.get('to').isValid()) {
115129
Cli.error(`TO is not a in valid ISO date format.`);
116130
}
117131

118-
// create stuff
119-
let reports = new ReportCollection(config),
120-
master = new Report(config),
121-
output;
122-
123132
// file prompt
124133
new Promise(resolve => {
125134
if (config.get('file') && fs.existsSync(config.get('file'))) {
@@ -133,36 +142,46 @@ new Promise(resolve => {
133142

134143
// get project(s)
135144
.then(() => new Promise((resolve, reject) => {
136-
Cli.list(`${Cli.look} Resolving "${config.get('project')}"`);
145+
Cli.list(`${Cli.look} Resolving "${projectLabels}"`);
137146
let owner = new Owner(config);
138147

139-
switch (config.get('type')) {
140-
case 'project':
141-
let report = new Report(config);
142-
reports.push(report);
143-
report.getProject()
144-
.then(() => resolve())
145-
.catch(e => reject(e));
146-
break;
147-
148-
case 'group':
149-
owner.getGroup()
150-
.then(() => {
151-
if (!config.get('subgroups')) return new Promise(r => r());
152-
return owner.getSubGroups();
153-
})
154-
.then(() => owner.getProjectsByGroup()
155-
.then(() => {
156-
owner.projects.forEach(project => {
157-
reports.push(new Report(config, project));
158-
});
159-
Cli.out(`\r${Cli.look} Selected projects: ${owner.listProjects()}\n`);
160-
resolve();
161-
}))
162-
.catch(e => reject(e));
163-
break;
164-
}
148+
owner
149+
.parallel(projects, (project, done) => {
150+
config.set('project', project);
151+
152+
switch (config.get('type')) {
153+
case 'project':
154+
let report = new Report(config);
155+
reports.push(report);
156+
report.getProject()
157+
.then(() => done())
158+
.catch(e => done(e));
159+
break;
160+
161+
case 'group':
162+
owner.getGroup()
163+
.then(() => {
164+
if (!config.get('subgroups')) return new Promise(r => r());
165+
return owner.getSubGroups();
166+
})
167+
.then(() => owner.getProjectsByGroup()
168+
.then(() => {
169+
owner.projects.forEach(project => {
170+
reports.push(new Report(config, project));
171+
});
172+
done();
173+
}))
174+
.catch(e => done(e));
175+
break;
176+
}
177+
}, 1)
178+
.catch(e => reject(e))
179+
.then(() => {
180+
config.set('project', projects);
181+
resolve();
182+
});
165183
}))
184+
.then(() => Cli.out(`\r${Cli.look} Selected projects: ${reports.reports.map(r => r.project.name.bold.blue).join(', ')}\n`))
166185

167186
// get members and user columns
168187
.then(() => new Promise(resolve => {
@@ -185,7 +204,7 @@ new Promise(resolve => {
185204
.then(() => resolve());
186205
}))
187206
.then(() => Cli.mark())
188-
.catch(error => Cli.x(`Could not resolve "${config.get('project')}"`, error))
207+
.catch(error => Cli.x(`Could not resolve "${projectLabels}"`, error))
189208

190209
// get issues
191210
.then(() => new Promise(resolve => {

include/cli.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ class cli {
214214
cli.resolve();
215215

216216
cli.out(`Error: ${message.red}` + '\n');
217-
if (error) console.log(error);
217+
if (error && cli.verbose) console.log(error);
218218

219219
process.exit(1);
220220
}
@@ -238,7 +238,10 @@ class cli {
238238

239239
if (this.data.project) return this.data.project;
240240

241-
return this.data.project = this.args.splice(0, 1).toString();
241+
let projects = _.uniq(_.filter(this.args, arg => !parseInt(arg)));
242+
this.args = _.difference(this.args, projects);
243+
244+
return this.data.project = projects;
242245
}
243246

244247
/**

include/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ const defaults = {
3434
quiet: false,
3535
showWithoutTimes: false,
3636
_perPage: 100,
37-
_parallel: 10
37+
_parallel: 10,
38+
_verbose: false
3839
};
3940

4041
/**

models/owner.js

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const _ = require('underscore');
22

33
const Base = require('./base');
4-
const Color = require('colors');
54

65
/**
76
* owner model
@@ -14,14 +13,6 @@ class owner extends Base {
1413
this.users = [];
1514
}
1615

17-
/**
18-
* list projects
19-
* @returns {string|*}
20-
*/
21-
listProjects() {
22-
return this.projects.map(p => p.path_with_namespace.bold.blue).join(',');
23-
}
24-
2516
/**
2617
* query and set the group
2718
* @returns {Promise}
@@ -30,7 +21,7 @@ class owner extends Base {
3021
return new Promise((resolve, reject) => {
3122
this.get(`groups/?search=${encodeURIComponent(this.config.get('project'))}`)
3223
.then(group => {
33-
if (group.body.length === 0) return reject();
24+
if (group.body.length === 0) return reject('Group not found');
3425
let filtered = group.body.filter(u => u.path === this.config.get('project'));
3526
if (filtered.length === 0) return reject();
3627
this.groups = this.groups.concat(filtered);

models/project.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ class project extends Base {
4343
return this.data.id;
4444
}
4545

46+
get name() {
47+
return this.data.path_with_namespace;
48+
}
49+
4650
get users() {
4751
return this.members.map(member => member.username);
4852
}

models/report.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class report extends Base {
6868
*/
6969
getProject() {
7070
let promise = this.get(`projects/${encodeURIComponent(this.config.get('project'))}`);
71-
promise.then(project => this.setProject(project));
71+
promise.then(project => this.setProject(project.body));
7272

7373
return promise;
7474
}

readme.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,17 @@ gtt report example-group --type=group --subgroups
208208
Query all projects from the given group and combine the data into a single report. The option `--subgroups`
209209
includes subgroups.
210210

211+
#### Query multiple projects or groups and combine the data in one report
212+
213+
```shell
214+
gtt report ["namespace/project"] ["namespace/project"] ...
215+
gtt report "kriskbx/example-project" "kriskbx/example-project-2"
216+
gtt report ["namespace"] ["namespace"] ... --type=group
217+
gtt report example-group example-group-2 --type=group
218+
```
219+
220+
*Hint: use the `project_id` or `project_namespace` columns in your report.*
221+
211222
#### Choose an output for your report
212223

213224
```shell
@@ -370,6 +381,12 @@ gtt report --exclude_labels=bug --exclude_labels=feature
370381
gtt report --proxy="http://localhost:8080"
371382
```
372383

384+
#### Output verbose debug information
385+
386+
```shell
387+
gtt report --verbose
388+
```
389+
373390
## configuration
374391

375392
The configuration uses the [yaml file format](http://www.yaml.org/spec/1.2/spec.html).
@@ -658,6 +675,12 @@ report.mergeRequests.forEach(mergeRequest => {
658675
It can include times outside the queried time frame. `spent` on the other hand
659676
is the total amount of time spent in the given time frame.
660677

678+
#### Why 'total spent' and 'spent' are showing different amounts.
679+
680+
gtt can only track time records from notes/comments. If you start your
681+
issue or merge request with `/spent [time]` in its description, gtt won't
682+
take it into consideration (for now).
683+
661684
## contributing
662685

663686
I would love to integrate unit testing in this project, but unfortunately my knowledge of

0 commit comments

Comments
 (0)