abt-cli 0.0.15 → 0.0.16
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.
- checksums.yaml +4 -4
- data/bin/abt +1 -1
- data/lib/abt.rb +4 -3
- data/lib/abt/cli.rb +70 -48
- data/lib/abt/cli/arguments_parser.rb +70 -0
- data/lib/abt/cli/base_command.rb +61 -0
- data/lib/abt/cli/prompt.rb +2 -2
- data/lib/abt/docs.rb +24 -18
- data/lib/abt/docs/cli.rb +42 -11
- data/lib/abt/docs/markdown.rb +36 -10
- data/lib/abt/git_config.rb +11 -0
- data/lib/abt/providers/asana/base_command.rb +13 -13
- data/lib/abt/providers/asana/commands/add.rb +3 -3
- data/lib/abt/providers/asana/commands/{branch-name.rb → branch_name.rb} +3 -3
- data/lib/abt/providers/asana/commands/clear.rb +17 -6
- data/lib/abt/providers/asana/commands/current.rb +3 -3
- data/lib/abt/providers/asana/commands/finalize.rb +3 -3
- data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +3 -3
- data/lib/abt/providers/asana/commands/init.rb +3 -3
- data/lib/abt/providers/asana/commands/pick.rb +13 -5
- data/lib/abt/providers/asana/commands/projects.rb +3 -3
- data/lib/abt/providers/asana/commands/share.rb +5 -5
- data/lib/abt/providers/asana/commands/start.rb +13 -7
- data/lib/abt/providers/asana/commands/tasks.rb +3 -3
- data/lib/abt/providers/asana/configuration.rb +5 -13
- data/lib/abt/providers/devops/base_command.rb +14 -15
- data/lib/abt/providers/devops/commands/boards.rb +6 -4
- data/lib/abt/providers/devops/commands/{branch-name.rb → branch_name.rb} +3 -3
- data/lib/abt/providers/devops/commands/clear.rb +17 -6
- data/lib/abt/providers/devops/commands/current.rb +3 -3
- data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +3 -3
- data/lib/abt/providers/devops/commands/init.rb +3 -3
- data/lib/abt/providers/devops/commands/pick.rb +12 -5
- data/lib/abt/providers/devops/commands/share.rb +4 -4
- data/lib/abt/providers/devops/commands/work-items.rb +3 -3
- data/lib/abt/providers/devops/configuration.rb +5 -13
- data/lib/abt/providers/git/commands/branch.rb +15 -21
- data/lib/abt/providers/harvest/base_command.rb +13 -13
- data/lib/abt/providers/harvest/commands/clear.rb +17 -6
- data/lib/abt/providers/harvest/commands/current.rb +3 -3
- data/lib/abt/providers/harvest/commands/init.rb +3 -3
- data/lib/abt/providers/harvest/commands/pick.rb +13 -5
- data/lib/abt/providers/harvest/commands/projects.rb +3 -3
- data/lib/abt/providers/harvest/commands/share.rb +5 -5
- data/lib/abt/providers/harvest/commands/start.rb +6 -42
- data/lib/abt/providers/harvest/commands/stop.rb +3 -3
- data/lib/abt/providers/harvest/commands/tasks.rb +3 -3
- data/lib/abt/providers/harvest/commands/track.rb +49 -11
- data/lib/abt/providers/harvest/configuration.rb +5 -11
- data/lib/abt/version.rb +1 -1
- metadata +6 -7
- data/lib/abt/providers/asana/commands/clear_global.rb +0 -24
- data/lib/abt/providers/devops/commands/clear_global.rb +0 -24
- data/lib/abt/providers/harvest/commands/clear_global.rb +0 -24
data/lib/abt/docs/cli.rb
CHANGED
@@ -4,23 +4,54 @@ module Abt
|
|
4
4
|
module Docs
|
5
5
|
module Cli
|
6
6
|
class << self
|
7
|
-
def
|
7
|
+
def help
|
8
8
|
<<~TXT
|
9
|
-
Usage:
|
9
|
+
Usage: #{usage_line}
|
10
10
|
|
11
|
-
|
11
|
+
<command> Name of command to execute, e.g. start, finalize etc.
|
12
|
+
<scheme-argument> A URI-like identifier; scheme:path
|
13
|
+
Points to a project/task etc. within a system.
|
14
|
+
<options> Optional flags for the command and scheme argument
|
12
15
|
|
13
|
-
|
14
|
-
|
16
|
+
#{formatted_examples(Docs.basic_examples)}
|
17
|
+
|
18
|
+
For detailed examples/commands try:
|
19
|
+
abt examples
|
20
|
+
abt commands
|
21
|
+
TXT
|
22
|
+
end
|
23
|
+
|
24
|
+
def examples
|
25
|
+
<<~TXT
|
26
|
+
Printing examples
|
27
|
+
|
28
|
+
#{formatted_examples(Docs.basic_examples)}
|
29
|
+
|
30
|
+
#{formatted_examples(Docs.extended_examples)}
|
31
|
+
TXT
|
32
|
+
end
|
33
|
+
|
34
|
+
def commands
|
35
|
+
<<~TXT
|
36
|
+
Printing commands
|
37
|
+
|
38
|
+
Run commands with --help flag to see detailed usage and flags, e.g.:
|
39
|
+
abt track harvest -h
|
40
|
+
|
41
|
+
#{commands_per_provider}
|
15
42
|
TXT
|
16
43
|
end
|
17
44
|
|
18
45
|
private
|
19
46
|
|
20
|
-
def
|
47
|
+
def usage_line
|
48
|
+
'abt <command> [<scheme-argument>] [<options> --] [<scheme-argument>] ...'
|
49
|
+
end
|
50
|
+
|
51
|
+
def formatted_examples(example_groups)
|
21
52
|
lines = []
|
22
53
|
|
23
|
-
|
54
|
+
example_groups.each_with_index do |(title, examples), index|
|
24
55
|
lines << '' unless index.zero?
|
25
56
|
lines << title
|
26
57
|
|
@@ -33,16 +64,16 @@ module Abt
|
|
33
64
|
lines.join("\n")
|
34
65
|
end
|
35
66
|
|
36
|
-
def
|
67
|
+
def commands_per_provider
|
37
68
|
lines = []
|
38
69
|
|
39
|
-
Docs.providers.each_with_index do |(
|
70
|
+
Docs.providers.each_with_index do |(scheme, commands_definition), index|
|
40
71
|
lines << '' unless index.zero?
|
41
|
-
lines << "#{inflector.humanize(
|
72
|
+
lines << "#{inflector.humanize(scheme)}:"
|
42
73
|
|
43
74
|
max_length = commands_definition.keys.map(&:length).max
|
44
75
|
|
45
|
-
commands_definition.each do |(command, description)|
|
76
|
+
commands_definition.each do |(command, (_usage, description))|
|
46
77
|
lines << " #{command.ljust(max_length)} #{description}"
|
47
78
|
end
|
48
79
|
end
|
data/lib/abt/docs/markdown.rb
CHANGED
@@ -4,18 +4,43 @@ module Abt
|
|
4
4
|
module Docs
|
5
5
|
module Markdown
|
6
6
|
class << self
|
7
|
-
def
|
7
|
+
def readme
|
8
8
|
<<~MD
|
9
9
|
# Abt
|
10
|
-
|
10
|
+
|
11
|
+
Abt makes re-occuring tasks easily accessible from the terminal:
|
12
|
+
- Moving asana tasks around
|
13
|
+
- Tracking work/meetings in harvest
|
14
|
+
- Consistently naming branches
|
15
|
+
|
16
|
+
## How does abt work?
|
17
|
+
|
18
|
+
Abt uses a hybrid approach between having small scripts each doing one thing:
|
19
|
+
- `start-asana --project-gid xxxx --task-gid yyyy`
|
20
|
+
- `start-harvest --project-id aaaa --task-id bbbb`
|
21
|
+
|
22
|
+
And having a single highly advanced script that does everything:
|
23
|
+
- `start xxxx/yyyy aaaa/bbbb`
|
24
|
+
|
25
|
+
Abt looks like one script, but works like a bunch of light independent scripts:
|
26
|
+
- `abt start asana:xxxx/yyyy harvest:aaaa/bbbb`
|
11
27
|
|
12
28
|
## Usage
|
13
|
-
`abt <command> [<
|
29
|
+
`abt <command> [<scheme-argument>] [<options> --] [<scheme-argument>] ...`
|
30
|
+
|
31
|
+
Definitions:
|
32
|
+
- `<command>`: Name of command to execute, e.g. `start`, `finalize` etc.
|
33
|
+
- `<scheme-argument>`: A URI-like identifier, `scheme:path`, pointing to a project/task etc. within a system.
|
34
|
+
- `<options>`: Optional flags for the command and scheme argument
|
14
35
|
|
15
36
|
#{example_commands}
|
16
37
|
|
17
38
|
## Available commands:
|
39
|
+
Some commands have `[options]`. Run such a command with `--help` flag to view supported flags, e.g: `abt track harvest -h`
|
40
|
+
|
18
41
|
#{provider_commands}
|
42
|
+
|
43
|
+
#### This readme was generated with `abt readme > README.md`
|
19
44
|
MD
|
20
45
|
end
|
21
46
|
|
@@ -24,7 +49,8 @@ module Abt
|
|
24
49
|
def example_commands
|
25
50
|
lines = []
|
26
51
|
|
27
|
-
Docs.
|
52
|
+
examples = Docs.basic_examples.merge(Docs.extended_examples)
|
53
|
+
examples.each_with_index do |(title, commands), index|
|
28
54
|
lines << '' unless index.zero?
|
29
55
|
lines << title
|
30
56
|
|
@@ -40,17 +66,17 @@ module Abt
|
|
40
66
|
def provider_commands # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
41
67
|
lines = []
|
42
68
|
|
43
|
-
Docs.providers.each_with_index do |(
|
69
|
+
Docs.providers.each_with_index do |(scheme, commands), index|
|
44
70
|
lines << '' unless index.zero?
|
45
|
-
lines << "### #{inflector.humanize(
|
71
|
+
lines << "### #{inflector.humanize(scheme)}"
|
46
72
|
lines << '| Command | Description |'
|
47
73
|
lines << '| :------ | :---------- |'
|
48
74
|
|
49
|
-
max_length = commands.
|
75
|
+
max_length = commands.values.map(&:first).map(&:length).max
|
50
76
|
|
51
|
-
commands.each do |(
|
52
|
-
|
53
|
-
lines << "| #{
|
77
|
+
commands.each do |(_command, (usage, description))|
|
78
|
+
adjusted_usage = "`#{usage}`".ljust(max_length + 2)
|
79
|
+
lines << "| #{adjusted_usage} | #{description} |"
|
54
80
|
end
|
55
81
|
end
|
56
82
|
|
data/lib/abt/git_config.rb
CHANGED
@@ -4,6 +4,8 @@ module Abt
|
|
4
4
|
class GitConfig
|
5
5
|
attr_reader :namespace, :scope
|
6
6
|
|
7
|
+
class UnsafeNamespaceError < StandardError; end
|
8
|
+
|
7
9
|
LOCAL_CONFIG_AVAILABLE_CHECK_COMMAND = 'git config --local -l'
|
8
10
|
|
9
11
|
def self.local_available?
|
@@ -67,6 +69,15 @@ module Abt
|
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
72
|
+
def clear(output: nil)
|
73
|
+
raise UnsafeNamespaceError, 'Keys can only be cleared within a namespace' if namespace.empty?
|
74
|
+
|
75
|
+
keys.each do |key|
|
76
|
+
output&.puts "Clearing #{scope}: #{key_with_namespace(key)}"
|
77
|
+
self[key] = nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
70
81
|
private
|
71
82
|
|
72
83
|
def ensure_scope_available!
|
@@ -3,19 +3,19 @@
|
|
3
3
|
module Abt
|
4
4
|
module Providers
|
5
5
|
module Asana
|
6
|
-
class BaseCommand
|
7
|
-
attr_reader :
|
6
|
+
class BaseCommand < Abt::Cli::BaseCommand
|
7
|
+
attr_reader :project_gid, :task_gid, :config
|
8
|
+
|
9
|
+
def initialize(path:, cli:, **)
|
10
|
+
super
|
8
11
|
|
9
|
-
def initialize(arg_str:, cli:)
|
10
|
-
@arg_str = arg_str
|
11
12
|
@config = Configuration.new(cli: cli)
|
12
13
|
|
13
|
-
if
|
14
|
-
|
14
|
+
if path.nil?
|
15
|
+
use_current_path
|
15
16
|
else
|
16
|
-
|
17
|
+
use_path(path)
|
17
18
|
end
|
18
|
-
@cli = cli
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
@@ -36,23 +36,23 @@ module Abt
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def print_project(project)
|
39
|
-
cli.
|
39
|
+
cli.print_scheme_argument('asana', project['gid'], project['name'])
|
40
40
|
cli.warn project['permalink_url'] if project.key?('permalink_url') && cli.output.isatty
|
41
41
|
end
|
42
42
|
|
43
43
|
def print_task(project, task)
|
44
44
|
project = { 'gid' => project } if project.is_a?(String)
|
45
|
-
cli.
|
45
|
+
cli.print_scheme_argument('asana', "#{project['gid']}/#{task['gid']}", task['name'])
|
46
46
|
cli.warn task['permalink_url'] if task.key?('permalink_url') && cli.output.isatty
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
49
|
+
def use_current_path
|
50
50
|
@project_gid = config.project_gid
|
51
51
|
@task_gid = config.task_gid
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
55
|
-
args =
|
54
|
+
def use_path(path)
|
55
|
+
args = path.to_s.split('/')
|
56
56
|
@project_gid = args[0].to_s
|
57
57
|
@project_gid = nil if project_gid.empty?
|
58
58
|
|
@@ -5,15 +5,15 @@ module Abt
|
|
5
5
|
module Asana
|
6
6
|
module Commands
|
7
7
|
class Add < BaseCommand
|
8
|
-
def self.
|
9
|
-
'add asana[:<project-gid>]'
|
8
|
+
def self.usage
|
9
|
+
'abt add asana[:<project-gid>]'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
13
|
'Create a new task for the current/specified Asana project'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def perform
|
17
17
|
require_project!
|
18
18
|
|
19
19
|
task
|
@@ -5,15 +5,15 @@ module Abt
|
|
5
5
|
module Asana
|
6
6
|
module Commands
|
7
7
|
class BranchName < BaseCommand
|
8
|
-
def self.
|
9
|
-
'branch-name asana[:<project-gid>/<task-gid>]'
|
8
|
+
def self.usage
|
9
|
+
'abt branch-name asana[:<project-gid>/<task-gid>]'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
13
|
'Suggest a git branch name for the current/specified task.'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def perform
|
17
17
|
require_task!
|
18
18
|
ensure_current_is_valid!
|
19
19
|
|
@@ -5,17 +5,28 @@ module Abt
|
|
5
5
|
module Asana
|
6
6
|
module Commands
|
7
7
|
class Clear < BaseCommand
|
8
|
-
def self.
|
9
|
-
'clear asana'
|
8
|
+
def self.usage
|
9
|
+
'abt clear asana'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
|
-
'Clear
|
13
|
+
'Clear asana configuration'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
16
|
+
def self.flags
|
17
|
+
[
|
18
|
+
['-g', '--global', 'Clear global instead of local asana configuration (credentials etc.)'],
|
19
|
+
['-a', '--all', 'Clear all asana configuration']
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
def perform
|
24
|
+
if flags[:global] && flags[:all]
|
25
|
+
abort('Flags --global and --all cannot be used at the same time')
|
26
|
+
end
|
27
|
+
|
28
|
+
config.clear_local unless flags[:global]
|
29
|
+
config.clear_global if flags[:global] || flags[:all]
|
19
30
|
end
|
20
31
|
end
|
21
32
|
end
|
@@ -5,15 +5,15 @@ module Abt
|
|
5
5
|
module Asana
|
6
6
|
module Commands
|
7
7
|
class Current < BaseCommand
|
8
|
-
def self.
|
9
|
-
'current asana[:<project-gid>[/<task-gid>]]'
|
8
|
+
def self.usage
|
9
|
+
'abt current asana[:<project-gid>[/<task-gid>]]'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
13
|
'Get or set project and or task for current git repository'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def perform
|
17
17
|
require_project!
|
18
18
|
|
19
19
|
if same_args_as_config? || !config.local_available?
|
@@ -5,15 +5,15 @@ module Abt
|
|
5
5
|
module Asana
|
6
6
|
module Commands
|
7
7
|
class Finalize < BaseCommand
|
8
|
-
def self.
|
9
|
-
'finalize asana[:<project-gid>/<task-gid>]'
|
8
|
+
def self.usage
|
9
|
+
'abt finalize asana[:<project-gid>/<task-gid>]'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
13
|
'Move current/specified task to section (column) for finalized tasks'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def perform
|
17
17
|
unless config.local_available?
|
18
18
|
cli.abort 'This is a no-op for tasks outside the current project'
|
19
19
|
end
|
@@ -5,15 +5,15 @@ module Abt
|
|
5
5
|
module Asana
|
6
6
|
module Commands
|
7
7
|
class HarvestTimeEntryData < BaseCommand
|
8
|
-
def self.
|
9
|
-
'harvest-time-entry-data asana[:<project-gid>/<task-gid>]'
|
8
|
+
def self.usage
|
9
|
+
'abt harvest-time-entry-data asana[:<project-gid>/<task-gid>]'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
13
|
'Print Harvest time entry data for Asana task as json. Used by harvest start script.'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def perform
|
17
17
|
require_task!
|
18
18
|
ensure_current_is_valid!
|
19
19
|
|
@@ -5,8 +5,8 @@ module Abt
|
|
5
5
|
module Asana
|
6
6
|
module Commands
|
7
7
|
class Init < BaseCommand
|
8
|
-
def self.
|
9
|
-
'init asana'
|
8
|
+
def self.usage
|
9
|
+
'abt init asana'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
@@ -18,7 +18,7 @@ module Abt
|
|
18
18
|
@cli = cli
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
21
|
+
def perform
|
22
22
|
cli.abort 'Must be run inside a git repository' unless config.local_available?
|
23
23
|
|
24
24
|
projects # Load projects up front to make it obvious that searches are instant
|
@@ -5,15 +5,21 @@ module Abt
|
|
5
5
|
module Asana
|
6
6
|
module Commands
|
7
7
|
class Pick < BaseCommand
|
8
|
-
def self.
|
9
|
-
'pick asana[:<project-gid>]'
|
8
|
+
def self.usage
|
9
|
+
'abt pick asana[:<project-gid>]'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
13
|
'Pick task for current git repository'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def self.flags
|
17
|
+
[
|
18
|
+
['-d', '--dry-run', 'Keep existing configuration']
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
def perform
|
17
23
|
cli.abort 'Must be run inside a git repository' unless config.local_available?
|
18
24
|
require_project!
|
19
25
|
|
@@ -21,10 +27,12 @@ module Abt
|
|
21
27
|
|
22
28
|
task = select_task
|
23
29
|
|
30
|
+
print_task(project, task)
|
31
|
+
|
32
|
+
return if flags[:"dry-run"]
|
33
|
+
|
24
34
|
config.project_gid = project_gid # We might have gotten the project ID as an argument
|
25
35
|
config.task_gid = task['gid']
|
26
|
-
|
27
|
-
print_task(project, task)
|
28
36
|
end
|
29
37
|
|
30
38
|
private
|
@@ -5,15 +5,15 @@ module Abt
|
|
5
5
|
module Asana
|
6
6
|
module Commands
|
7
7
|
class Projects < BaseCommand
|
8
|
-
def self.
|
9
|
-
'projects asana'
|
8
|
+
def self.usage
|
9
|
+
'abt projects asana'
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
13
|
'List all available projects - useful for piping into grep etc.'
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def perform
|
17
17
|
projects.map do |project|
|
18
18
|
print_project(project)
|
19
19
|
end
|