Skip to content
This repository was archived by the owner on May 22, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM node:8.2.1-alpine

ENV GTT_VERSION 1.7.2
ENV GTT_VERSION 1.7.4

RUN yarn global add --prefix /usr/local "gitlab-time-tracker@$GTT_VERSION"

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gitlab-time-tracker",
"version": "1.7.2",
"version": "1.7.4",
"description": "A command line interface for GitLabs time tracking feature.",
"bugs": {
"url": "https://github.com/kriskbx/gitlab-time-tracker/issues"
Expand Down
Binary file added preview/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 7 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# gtt
# ![gtt](https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker/master/preview/icon.png) gtt

[![npm](https://img.shields.io/npm/dt/gitlab-time-tracker.svg?style=flat-square)](https://www.npmjs.com/package/gitlab-time-tracker)
[![npm](https://img.shields.io/npm/v/gitlab-time-tracker.svg?style=flat-square)](https://www.npmjs.com/package/gitlab-time-tracker)
Expand Down Expand Up @@ -30,6 +30,7 @@ stored on GitLab.
* [config file](#config-file)
* [time format](#time-format)
* [how to use gtt as a library](#how-to-use-gtt-as-a-library)
* [dumps](#dumps)
* [faqs](#faqs)
* [contributing](#contributing)
* [buy me a beer 🍺](#buy-me-a-beer)
Expand Down Expand Up @@ -72,7 +73,7 @@ token: 01234567891011
## updating
**Updating from version <= 1.5? Please [click here](https://github.com/kriskbx/gitlab-time-tracker/blob/master/upgrade.md)!**
**Updating from version <= 1.5? Please [click here](https://github.com/kriskbx/gitlab-time-tracker/blob/master/UPGRADE.md)!**
Update gtt via yarn:
Expand Down Expand Up @@ -796,6 +797,10 @@ report.mergeRequests.forEach(mergeRequest => {
});
```

## dumps

Starting with 1.7.4 gtt can dump the results of all API requests within a report and use it on another machine without access to the GitLab instance itself. This is very useful for debugging purposes. If you stumble upon a bug which could be unique to your set of data, please rerun the report with these options to save a dump to the given file: `--output=dump --file=/path/dump.json` Check your dump for sensitive information and provide it when asked.

## faqs

#### What is the difference between 'total spent' and 'spent'?
Expand Down
19 changes: 16 additions & 3 deletions src/gtt-report.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ const Output = {
table: require('./output/table'),
csv: require('./output/csv'),
pdf: require('./output/pdf'),
markdown: require('./output/markdown')
markdown: require('./output/markdown'),
dump: require('./output/dump')
};

// this collects options
Expand Down Expand Up @@ -56,12 +57,24 @@ program
.option('--check_token', 'check the access token')
.option('--show_without_times', 'show issues/merge requests without time records')
.option('-p --proxy <proxy>', 'use a proxy server with the given url')
.option('--from_dump <file>', 'instead of querying gitlab, use data from the given dump file')
.parse(process.argv);

// init helpers
let config = new Config(process.cwd());
let cli = new Cli(program.args);

// if using a dump, set the config accordingly
if (program.from_dump && fs.existsSync(program.from_dump)) {
let data = JSON.parse(fs.readFileSync(program.from_dump));

if (data.data) _.each(data.data, (v, i) => {
config.set(i, v);
});

if (data._dump) config.dump = data._dump;
}

// overwrite config with args and opts
config
.set('project', cli.project())
Expand Down Expand Up @@ -94,12 +107,12 @@ config
.set('type', program.type)
.set('subgroups', program.subgroups)
.set('_verbose', program.verbose)
.set('_checkToken', program.check_token);
.set('_checkToken', program.check_token)
.set('_createDump', program.output === 'dump');

Cli.quiet = config.get('quiet');
Cli.verbose = config.get('_verbose');


// create stuff
let reports = new ReportCollection(config),
master = new Report(config),
Expand Down
2 changes: 1 addition & 1 deletion src/gtt.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node

const version = '1.7.2';
const version = '1.7.4';
const program = require('commander');

program
Expand Down
82 changes: 65 additions & 17 deletions src/models/base.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const request = require('request-promise-native');
const url = require('url');
const async = require('async');
const crypto = require('crypto');

/**
* base model
Expand Down Expand Up @@ -29,17 +30,25 @@ class base {
* @returns {*}
*/
post(path, data) {
let key = base.createDumpKey(path, data);
if (this.config.dump) return this.getDump(key);

data.private_token = this.token;

return request.post(`${this.url}${path}`, {
json: true,
body: data,
insecure: this._insecure,
proxy: this._proxy,
resolveWithFullResponse: true,
headers: {
'PRIVATE-TOKEN': this.token
}
return new Promise((resolve, reject) => {
request.post(`${this.url}${path}`, {
json: true,
body: data,
insecure: this._insecure,
proxy: this._proxy,
resolveWithFullResponse: true,
headers: {
'PRIVATE-TOKEN': this.token
}
}).then(response => {
if (this.config.get('_createDump')) this.setDump(response, key);
resolve(response);
}).catch(e => reject(e));
});
}

Expand All @@ -51,17 +60,25 @@ class base {
* @returns {Promise}
*/
get(path, page = 1, perPage = this._perPage) {
let key = base.createDumpKey(path, page, perPage);
if (this.config.dump) return this.getDump(key);

path += (path.includes('?') ? '&' : '?') + `private_token=${this.token}`;
path += `&page=${page}&per_page=${perPage}`;

return request(`${this.url}${path}`, {
json: true,
insecure: this._insecure,
proxy: this._proxy,
resolveWithFullResponse: true,
headers: {
'PRIVATE-TOKEN': this.token
}
return new Promise((resolve, reject) => {
request(`${this.url}${path}`, {
json: true,
insecure: this._insecure,
proxy: this._proxy,
resolveWithFullResponse: true,
headers: {
'PRIVATE-TOKEN': this.token
}
}).then(response => {
if (this.config.get('_createDump')) this.setDump(response, key);
resolve(response);
}).catch(e => reject(e));
});
}

Expand Down Expand Up @@ -124,6 +141,29 @@ class base {
}, runners);
}

/**
* save the given response to dump
* @param response
* @param key
*/
setDump(response, key) {
if (!this.config._dump) this.config._dump = {};

this.config._dump[key] = {
headers: response.headers,
body: response.body
};
}

/**
* get from dump
* @param key
* @returns {Promise}
*/
getDump(key) {
return new Promise(r => r(this.config.dump[key]));
}

/**
* create a task list to get all pages from
* the given path
Expand All @@ -142,6 +182,14 @@ class base {

return tasks;
}

/**
* create a key representing a request
* @param args
*/
static createDumpKey(...args) {
return crypto.createHash('md5').update(JSON.stringify(args)).digest("hex");
}
}

module.exports = base;
13 changes: 13 additions & 0 deletions src/output/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,22 @@ class base {
totalEstimate += parseInt(issue.stats.time_estimate);
totalSpent += parseInt(issue.stats.total_time_spent);
});

this.report[type].sort((a, b) => {
if (a.iid === b.iid) return 0;

return (a.iid - b.iid) < 0 ? 1 : -1;
});
});


this.times = times;
this.times.sort((a, b) => {
if (a.date.isSame(b.date)) return 0;

return a.date.isBefore(b.date) ? 1 : -1;
});

this.users = _.mapObject(users, user => this.config.toHumanReadable(user, 'stats'));
this.projects = _.mapObject(projects, project => this.config.toHumanReadable(project, 'stats'));
this.stats = {
Expand Down
30 changes: 30 additions & 0 deletions src/output/dump.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const Base = require('./base');
const Report = require('../models/report');

class dump extends Base {
constructor(config, report) {
super(config, report);

config.set('url', null, true);
config.set('token', null, true);
config.set('_createDump', false);
config.workDir = null;
config.cache = null;

this.write(JSON.stringify(config));
}

makeStats() {
}

makeIssues() {
}

makeMergeRequests() {
}

makeRecords() {
}
}

module.exports = dump;