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
Show all changes
34 commits
Select commit Hold shift + click to select a range
56d54f5
Typo : total_estimate instead of estimation
benchti Oct 2, 2017
ff86366
Merge pull request #28 from benchti/patch-1
kriskbx Oct 3, 2017
9540c03
Add recursive subgroup querying
kriskbx Oct 3, 2017
565ffc6
Release 1.6.11
kriskbx Oct 3, 2017
7855f6b
Add timeformat for different report parts
kriskbx Oct 3, 2017
0833099
Add shorthands for gtt-start command. #23
kriskbx Oct 3, 2017
8c835d7
Add description parsing #27
kriskbx Oct 3, 2017
7e26755
Remove FAQ part from readme
kriskbx Oct 3, 2017
b956d32
open config file in local EDITOR, if user is connected via SSH
zealot128 Oct 6, 2017
e7d8c01
Implemented resume command
zealot128 Oct 6, 2017
acade2b
made gtt-resume.js executable
zealot128 Oct 6, 2017
4d2dc51
Merge branch 'gtt-resume' of https://github.com/zealot128-os/gitlab-t…
kriskbx Oct 6, 2017
3f49200
Add project argument to resume command. Refactor resume method to be …
kriskbx Oct 6, 2017
9a55e39
Add promise rejection to resume method if there's no project set
kriskbx Oct 6, 2017
02d1502
Merge branch 'zealot128-os-gtt-resume'
kriskbx Oct 6, 2017
66fd3c1
Fix issues with description parsing
kriskbx Oct 10, 2017
ee35f64
Merge branch 'ssh-login-editor' of https://github.com/zealot128-os/gi…
kriskbx Oct 10, 2017
3b8a381
Add VISUAL and EDITOR env check to open command
kriskbx Oct 10, 2017
06de05b
Fix group searching by using the full path
kriskbx Oct 10, 2017
15a3d25
Add time format ceiling
kriskbx Oct 12, 2017
17bec44
Add rounded conditionals to time format
kriskbx Oct 14, 2017
3617da5
Fix missing time when there's no conditional in the ceiling function
kriskbx Oct 19, 2017
6cf60b8
Release 1.7.0
kriskbx Oct 19, 2017
4ed1d1c
Fix missing time_stats
kriskbx Oct 23, 2017
d525463
Fix missing time_stats
kriskbx Oct 23, 2017
c9fd848
Fix 'total_time_spent' undefined error
kriskbx Nov 12, 2017
1670b89
Release 1.7.1
kriskbx Nov 12, 2017
9c78e89
Fix configuration directory permissions
bobvandevijver Dec 12, 2017
16ee9cf
Merge pull request #37 from bobvandevijver/patch-1
kriskbx Dec 15, 2017
e5527de
Release 1.7.2
kriskbx Dec 15, 2017
ba2feef
Added line on default editor
bobvandevijver Dec 31, 2017
e68fb7a
Merge pull request #40 from bobvandevijver/patch-2
kriskbx Jan 11, 2018
2d64d5b
Add support for self-signed ssl certificates
kriskbx Jan 11, 2018
fce944a
Javascript parseInt also returns true if only the first character of …
spamsch Jan 12, 2018
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.6.10
ENV GTT_VERSION 1.7.2

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.6.10",
"version": "1.7.2",
"description": "A command line interface for GitLabs time tracking feature.",
"bugs": {
"url": "https://github.com/kriskbx/gitlab-time-tracker/issues"
Expand Down
63 changes: 49 additions & 14 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ npm install -g gitlab-time-tracker
```

Run the config command to create a config file and open it in your default editor.
In linux terminal, you must set your preferred editor in the environment. For example, use `export EDITOR=vim` to edit the files with vim (put this in `.bashrc` or similar to have it always configured).
If nothing happens, open the file manually: `~/.gtt/config.yml` - on Windows: `C:\Users\YourUserName\.gtt\config.yml`

```shell
Expand Down Expand Up @@ -484,6 +485,10 @@ token: abcdefghijklmnopqrst
# defaults to false
proxy: http://localhost:8080

# Don't check SSL certificate
# defaults to false
insecure: true

# Project
# defaults to false
project: namespace/projectname
Expand Down Expand Up @@ -543,15 +548,15 @@ hoursPerDay: 8
issueColumns:
- iid
- title
- estimation
- total_estimate

# Include the given columns in the merge request table
# See --merge_request_columns option for more information
# defaults to iid, title, spent, total_estimate
mergeRequestColumns:
- iid
- title
- estimation
- total_estimate

# Include the given columns in the time record table
# See --record_columns option for more information
Expand All @@ -576,6 +581,16 @@ dateFormat: DD.MM.YYYY HH:mm:ss
# defaults to "[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]"
timeFormat: "[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]"

# Time format for different parts of the report
# Instead of specifying one global time format you can specify one for every
# part of the report and the log command
timeFormat:
log: "[%sign][%hours_overall]"
stats: "[%sign][%days_overall]"
issues: "[%sign][%hours_overall]"
merge_requests: "[%sign][%hours_overall]"
records: "[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]"

# Output type
# Available: csv, table, markdown, pdf
# defaults to table
Expand All @@ -593,6 +608,12 @@ includeLabels:
- pending
- approved

# Only works if using a local configuration file!
# Extend the global configuration if set to true, pass a string to extend
# the configuration file stored at the given path
# defaults to true
extend: true

# Change number of concurrent connections/http queries
# Note: Handle with care, we don't want to spam GitLabs API too much
# defaults to 10
Expand All @@ -602,11 +623,14 @@ _parallel: 20
# defaults to 100
_perPage: 100

# Only works if using a local configuration file!
# Extend the global configuration if set to true, pass a string to extend
# the configuration file stored at the given path
# defaults to true
extend: true
# Verbose output
_verbose: false

# Check access token validity up front
_checkToken: false

# Skip parsing the issue/merge_request description for time records
_skipDescriptionParsing: false
```

### Time format
Expand Down Expand Up @@ -698,6 +722,23 @@ timeFormat: "[%sign][%minutes_overall]"
1095
```

##### `[%hours_overall:2]`, `[%days_overall:3]`

You can ceil any float value by adding the number of decimals to keep separated with a `:`.

**Example config:**

```yaml
timeFormat: "[%sign][%hours_overall:2]"
```

**Example outputs:**

```shell
0,51
18,25
```

## how to use gtt as a library

Add as a dependency using yarn:
Expand Down Expand Up @@ -763,12 +804,6 @@ report.mergeRequests.forEach(mergeRequest => {
It can include times outside the queried time frame. `spent` on the other hand
is the total amount of time spent in the given time frame.

#### Why 'total spent' and 'spent' are showing different amounts.

gtt can only track time records from notes/comments. If you start your
issue or merge request with `/spend [time]` in its description, gtt won't
take it into consideration (for now).

## contributing

I would love to integrate unit testing in this project, but unfortunately my knowledge of
Expand All @@ -789,4 +824,4 @@ or already have done so. 💜

## license

GPL v2
GPL v2
27 changes: 26 additions & 1 deletion spec/include/config.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,33 @@ describe('The config class', () => {
});
});

it('returns values from objects and falls back to the default', () => {
let Config = new config(),
objectsWithDefaults = ['timeFormat'],
defaults = Object.assign({}, Config.data),
stringData = 'booze',
objectData = {
foo: 'bar',
baz: 'bar'
};

objectsWithDefaults.forEach(key => {
Config.set(key, objectData);

expect(Config.get(key, 'foo')).to.equal('bar');
expect(Config.get(key, 'baz')).to.equal('bar');
expect(Config.get(key, 'not_a_real_key')).to.equal(defaults[key]);

Config.set(key, stringData);

expect(Config.get(key, 'foo')).to.equal('booze');
expect(Config.get(key, 'baz')).to.equal('booze');
expect(Config.get(key, 'not_a_real_key')).to.equal('booze');
});
});

it('makes durations human readable', () => {
let Config = new config,
let Config = new config(),
humanReadable = "1d 4h 30m 10s",
seconds = 45010;

Expand Down
9 changes: 5 additions & 4 deletions src/gtt-log.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ const Tasks = require('./include/tasks');
program
.option('--verbose', 'show verbose output')
.option('--hours_per_day <hours>', 'hours per day for human readable time formats')
.option('--time_format <time_format>', 'time format')
.parse(process.argv);

Cli.verbose = program.verbose;

let config = new Config(__dirname)
.set('hoursPerDay', program.hours_per_day),
tasks = new Tasks(config);
let config = new Config(__dirname).set('hoursPerDay', program.hours_per_day),
tasks = new Tasks(config),
timeFormat = config.set('timeFormat', program.time_format).get('timeFormat', 'log');

function toHumanReadable(input) {
return Time.toHumanReadable(Math.ceil(input), config.get('hoursPerDay'), config.get('timeFormat'));
return Time.toHumanReadable(Math.ceil(input), config.get('hoursPerDay'), timeFormat);
}

tasks.log()
Expand Down
24 changes: 24 additions & 0 deletions src/gtt-resume.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const program = require('commander');
const colors = require('colors');
const moment = require('moment');

const Config = require('./include/file-config');
const Cli = require('./include/cli');
const Tasks = require('./include/tasks');

program
.arguments('[project]')
.option('--verbose', 'show verbose output')
.parse(process.argv);

Cli.verbose = program.verbose;

let config = new Config(process.cwd()).set('project', program.args[0]),
tasks = new Tasks(config);

if (!config.get('project'))
Cli.error('No project set');

tasks.resume()
.then(frame => console.log(`Starting project ${config.get('project').magenta} ${frame.resource.type.blue} ${('#' + frame.resource.id).blue} at ${moment().format('HH:mm').green}`))
.catch(error => Cli.error(error));
8 changes: 8 additions & 0 deletions src/gtt-start.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const Tasks = require('./include/tasks');
program
.arguments('[project] [id]')
.option('-t, --type <type>', 'specify resource type: issue, merge_request')
.option('-m', 'shorthand for --type=merge_request')
.option('-i', 'shorthand for --type=issue')
.option('--verbose', 'show verbose output')
.parse(process.argv);

Expand All @@ -20,6 +22,12 @@ let config = new Config(process.cwd()),
id = program.args.length === 1 ? parseInt(program.args[0]) : parseInt(program.args[1]),
project = program.args.length === 2 ? program.args[0] : null;

if (program.I) {
type = 'issue';
} else if (program.M) {
type = 'merge_request';
}

if (program.args.length < 2 && !config.get('project'))
Cli.error('No project set');

Expand Down
3 changes: 2 additions & 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.6.10';
const version = '1.7.2';
const program = require('commander');

program
Expand All @@ -9,6 +9,7 @@ program
.command('create [project] [title]', 'start monitoring time for the given project and create a new issue or merge request with the given title')
.command('status', 'shows if time monitoring is running')
.command('stop', 'stop monitoring time')
.command('resume [project]', 'resume monitoring time for last stopped record')
.command('cancel', 'cancel and discard active monitoring time')
.command('log', 'log recorded time records')
.command('sync', 'sync local time records to GitLab')
Expand Down
2 changes: 1 addition & 1 deletion src/include/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ class cli {

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

let projects = _.uniq(_.filter(this.args, arg => !parseInt(arg)));
let projects = _.uniq(_.filter(this.args, arg => !Number.isNaN(new Number(arg))));
this.args = _.difference(this.args, projects);

return this.data.project = projects;
Expand Down
24 changes: 17 additions & 7 deletions src/include/config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
const moment = require('moment');
const _ = require('underscore');

const Time = require('./../models/time');

const dates = ['from', 'to'];
const objectsWithDefaults = ['timeFormat', 'columns'];
const defaults = {
type: 'project',
subgroups: false,
url: 'https://gitlab.com/api/v4',
token: false,
proxy: false,
insecure: false,
project: false,
from: "1970-01-01",
to: moment().format(),
Expand Down Expand Up @@ -36,7 +39,8 @@ const defaults = {
_perPage: 100,
_parallel: 10,
_verbose: false,
_checkToken: false
_checkToken: false,
_skipDescriptionParsing: false
};

/**
Expand All @@ -47,7 +51,7 @@ class config {
* construct
*/
constructor() {
this.data = defaults;
this.data = _.extend({}, defaults);
}

/**
Expand All @@ -70,21 +74,27 @@ class config {
/**
* get a value by the given key
* @param key
* @param subKey
* @returns {*}
*/
get(key) {
if (!dates.includes(key)) return this.data[key];
get(key, subKey = false) {
if (dates.includes(key))
return moment(this.data[key]);

return moment(this.data[key]);
if (objectsWithDefaults.includes(key) && _.isObject(this.data[key]))
return subKey && this.data[key][subKey] ? this.data[key][subKey] : defaults[key];

return this.data[key];
}

/**
* get a human readable version of the given time
* @param input
* @param timeFormat
* @returns {string}
*/
toHumanReadable(input) {
return Time.toHumanReadable(input, this.get('hoursPerDay'), this.get('timeFormat'));
toHumanReadable(input, timeFormat = false) {
return Time.toHumanReadable(input, this.get('hoursPerDay'), this.get('timeFormat', timeFormat));
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/include/file-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ class fileConfig extends config {
}

assertGlobalConfig() {
if (!fs.existsSync(this.globalDir)) fs.mkdirSync(this.globalDir, '0644', true);
if (!fs.existsSync(this.frameDir)) fs.mkdirSync(this.frameDir, '0744', true);
if (!fs.existsSync(this.cacheDir)) fs.mkdirSync(this.cacheDir, '0744', true);
if (!fs.existsSync(this.globalDir)) fs.mkdirSync(this.globalDir, '0750', true);
if (!fs.existsSync(this.frameDir)) fs.mkdirSync(this.frameDir, '0750', true);
if (!fs.existsSync(this.cacheDir)) fs.mkdirSync(this.cacheDir, '0750', true);
if (!fs.existsSync(this.global)) fs.appendFileSync(this.global, '');
}

Expand Down Expand Up @@ -133,4 +133,4 @@ class fileConfig extends config {
}
}

module.exports = fileConfig;
module.exports = fileConfig;
13 changes: 11 additions & 2 deletions src/include/filesystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const fs = require('fs');
const path = require('path');
const open = require('open');
const find = require('find-in-files');
const child_process = require('child_process');

class filesystem {
static find(pattern, dir) {
Expand All @@ -22,7 +23,15 @@ class filesystem {
}

static open(file) {
return open(file);
let editor = process.env.VISUAL;

if (editor || (editor = process.env.EDITOR)) {
return child_process.spawn(editor, [file], {
stdio: 'inherit'
});
} else {
return open(file);
}
}

static join(...args) {
Expand All @@ -38,4 +47,4 @@ class filesystem {
}
}

module.exports = filesystem;
module.exports = filesystem;
Loading