sfctl 0.0.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.
- checksums.yaml +7 -0
- data/.buildkite/hooks/pre-command +5 -0
- data/.buildkite/pipeline.yml +9 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +76 -0
- data/.tool-versions +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +40 -0
- data/Gemfile.lock +186 -0
- data/README.md +276 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/sfctl +18 -0
- data/lib/sfctl.rb +5 -0
- data/lib/sfctl/cli.rb +44 -0
- data/lib/sfctl/command.rb +166 -0
- data/lib/sfctl/commands/account.rb +34 -0
- data/lib/sfctl/commands/account/assignments.rb +52 -0
- data/lib/sfctl/commands/account/info.rb +40 -0
- data/lib/sfctl/commands/auth.rb +35 -0
- data/lib/sfctl/commands/auth/bye.rb +26 -0
- data/lib/sfctl/commands/auth/init.rb +53 -0
- data/lib/sfctl/commands/time.rb +52 -0
- data/lib/sfctl/commands/time/connections.rb +33 -0
- data/lib/sfctl/commands/time/connections/add.rb +84 -0
- data/lib/sfctl/commands/time/connections/get.rb +53 -0
- data/lib/sfctl/commands/time/init.rb +23 -0
- data/lib/sfctl/commands/time/providers.rb +44 -0
- data/lib/sfctl/commands/time/providers/get.rb +41 -0
- data/lib/sfctl/commands/time/providers/set.rb +58 -0
- data/lib/sfctl/commands/time/providers/unset.rb +43 -0
- data/lib/sfctl/commands/time/sync.rb +215 -0
- data/lib/sfctl/starfish.rb +51 -0
- data/lib/sfctl/templates/.gitkeep +1 -0
- data/lib/sfctl/toggl.rb +27 -0
- data/lib/sfctl/version.rb +3 -0
- data/scripts/test.sh +8 -0
- data/sfctl.gemspec +28 -0
- metadata +84 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "sfctl"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/exe/sfctl
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
lib_path = File.expand_path('../lib', __dir__)
|
5
|
+
$:.unshift(lib_path) if !$:.include?(lib_path)
|
6
|
+
require 'sfctl/cli'
|
7
|
+
|
8
|
+
Signal.trap('INT') do
|
9
|
+
warn("\n#{caller.join("\n")}: interrupted")
|
10
|
+
exit(1)
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
Sfctl::CLI.start
|
15
|
+
rescue Sfctl::CLI::Error => err
|
16
|
+
puts "ERROR: #{err.message}"
|
17
|
+
exit 1
|
18
|
+
end
|
data/lib/sfctl.rb
ADDED
data/lib/sfctl/cli.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require 'pastel'
|
5
|
+
require 'tty-font'
|
6
|
+
|
7
|
+
module Sfctl
|
8
|
+
# Handle the application command line parsing
|
9
|
+
# and the dispatch to various command objects
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
class CLI < Thor
|
13
|
+
# Error raised by this runner
|
14
|
+
Error = Class.new(StandardError)
|
15
|
+
|
16
|
+
def help(*args)
|
17
|
+
font = TTY::Font.new(:standard)
|
18
|
+
pastel = Pastel.new(enabled: !options['no-color'])
|
19
|
+
puts pastel.yellow(font.write('sfctl'))
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
class_option :"no-color", type: :boolean, default: false, desc: 'Disable colorization in output'
|
24
|
+
class_option :"starfish-host", type: :string, default: 'https://starfish.team',
|
25
|
+
desc: 'The starfish API endpoint',
|
26
|
+
banner: 'HOST'
|
27
|
+
|
28
|
+
desc 'version', 'sfctl version'
|
29
|
+
def version
|
30
|
+
require_relative 'version'
|
31
|
+
puts "v#{Sfctl::VERSION}"
|
32
|
+
end
|
33
|
+
map %w[--version -v] => :version
|
34
|
+
|
35
|
+
require_relative 'commands/time'
|
36
|
+
register Sfctl::Commands::Time, 'time', 'time [SUBCOMMAND]', 'Time reports'
|
37
|
+
|
38
|
+
require_relative 'commands/account'
|
39
|
+
register Sfctl::Commands::Account, 'account', 'account [SUBCOMMAND]', 'Account information for Starfish.team'
|
40
|
+
|
41
|
+
require_relative 'commands/auth'
|
42
|
+
register Sfctl::Commands::Auth, 'auth', 'auth [SUBCOMMAND]', 'Authentication with Starfish.team'
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'tty-config'
|
5
|
+
|
6
|
+
module Sfctl
|
7
|
+
class Command
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
CONFIG_FILENAME = '.sfctl'
|
11
|
+
CONFIG_PATH = "#{Dir.home}/#{CONFIG_FILENAME}"
|
12
|
+
LINK_CONFIG_FILENAME = '.sflink'
|
13
|
+
LINK_CONFIG_PATH = "#{Dir.home}/#{LINK_CONFIG_FILENAME}"
|
14
|
+
|
15
|
+
TOGGL_PROVIDER = 'toggl'
|
16
|
+
PROVIDERS_LIST = [
|
17
|
+
TOGGL_PROVIDER
|
18
|
+
].freeze
|
19
|
+
|
20
|
+
def_delegators :command, :run
|
21
|
+
|
22
|
+
# Main configuration
|
23
|
+
# @api public
|
24
|
+
def config
|
25
|
+
@config ||= begin
|
26
|
+
config = TTY::Config.new
|
27
|
+
config.append_path Dir.home
|
28
|
+
config
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def save_config!
|
33
|
+
config.write(CONFIG_PATH, format: :yaml, force: true)
|
34
|
+
end
|
35
|
+
|
36
|
+
def read_config
|
37
|
+
config.read(CONFIG_PATH, format: :yaml)
|
38
|
+
end
|
39
|
+
|
40
|
+
def access_token
|
41
|
+
read_config['access_token']
|
42
|
+
end
|
43
|
+
|
44
|
+
def config_present?(output)
|
45
|
+
read_config
|
46
|
+
rescue TTY::Config::ReadError
|
47
|
+
output.puts Pastel.new(enabled: !@options['no-color']).red('Please authentificate before continue.')
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
def read_link_config
|
52
|
+
config.read(LINK_CONFIG_PATH, format: :yaml)
|
53
|
+
end
|
54
|
+
|
55
|
+
def save_link_config!
|
56
|
+
config.write(LINK_CONFIG_PATH, format: :yaml, force: true)
|
57
|
+
end
|
58
|
+
|
59
|
+
def link_config_present?(output)
|
60
|
+
read_link_config
|
61
|
+
rescue TTY::Config::ReadError
|
62
|
+
output.puts Pastel.new(enabled: !@options['no-color']).red('Please initialize time before continue.')
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
# Execute this command
|
67
|
+
#
|
68
|
+
# @api public
|
69
|
+
def execute(*)
|
70
|
+
raise(
|
71
|
+
NotImplementedError,
|
72
|
+
"#{self.class}##{__method__} must be implemented"
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
# The external commands runner
|
77
|
+
#
|
78
|
+
# @see http://www.rubydoc.info/gems/tty-command
|
79
|
+
#
|
80
|
+
# @api public
|
81
|
+
def command(**options)
|
82
|
+
require 'tty-command'
|
83
|
+
TTY::Command.new(options)
|
84
|
+
end
|
85
|
+
|
86
|
+
# The cursor movement
|
87
|
+
#
|
88
|
+
# @see http://www.rubydoc.info/gems/tty-cursor
|
89
|
+
#
|
90
|
+
# @api public
|
91
|
+
# def cursor
|
92
|
+
# require 'tty-cursor'
|
93
|
+
# TTY::Cursor
|
94
|
+
# end
|
95
|
+
|
96
|
+
# Open a file or text in the user's preferred editor
|
97
|
+
#
|
98
|
+
# @see http://www.rubydoc.info/gems/tty-editor
|
99
|
+
#
|
100
|
+
# @api public
|
101
|
+
# def editor
|
102
|
+
# require 'tty-editor'
|
103
|
+
# TTY::Editor
|
104
|
+
# end
|
105
|
+
|
106
|
+
# Terminal output paging
|
107
|
+
#
|
108
|
+
# @see http://www.rubydoc.info/gems/tty-pager
|
109
|
+
#
|
110
|
+
# @api public
|
111
|
+
# def pager(**options)
|
112
|
+
# require 'tty-pager'
|
113
|
+
# TTY::Pager.new(options)
|
114
|
+
# end
|
115
|
+
|
116
|
+
# Terminal platform and OS properties
|
117
|
+
#
|
118
|
+
# @see http://www.rubydoc.info/gems/tty-pager
|
119
|
+
#
|
120
|
+
# @api public
|
121
|
+
# def platform
|
122
|
+
# require 'tty-platform'
|
123
|
+
# TTY::Platform.new
|
124
|
+
# end
|
125
|
+
|
126
|
+
# The interactive prompt
|
127
|
+
#
|
128
|
+
# @see http://www.rubydoc.info/gems/tty-prompt
|
129
|
+
#
|
130
|
+
# @api public
|
131
|
+
# def prompt(**options)
|
132
|
+
# require 'tty-prompt'
|
133
|
+
# TTY::Prompt.new(options)
|
134
|
+
# end
|
135
|
+
|
136
|
+
# Get terminal screen properties
|
137
|
+
#
|
138
|
+
# @see http://www.rubydoc.info/gems/tty-screen
|
139
|
+
#
|
140
|
+
# @api public
|
141
|
+
# def screen
|
142
|
+
# require 'tty-screen'
|
143
|
+
# TTY::Screen
|
144
|
+
# end
|
145
|
+
|
146
|
+
# The unix which utility
|
147
|
+
#
|
148
|
+
# @see http://www.rubydoc.info/gems/tty-which
|
149
|
+
#
|
150
|
+
# @api public
|
151
|
+
# def which(*args)
|
152
|
+
# require 'tty-which'
|
153
|
+
# TTY::Which.which(*args)
|
154
|
+
# end
|
155
|
+
|
156
|
+
# Check if executable exists
|
157
|
+
#
|
158
|
+
# @see http://www.rubydoc.info/gems/tty-which
|
159
|
+
#
|
160
|
+
# @api public
|
161
|
+
# def exec_exist?(*args)
|
162
|
+
# require 'tty-which'
|
163
|
+
# TTY::Which.exist?(*args)
|
164
|
+
# end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Sfctl
|
4
|
+
module Commands
|
5
|
+
class Account < Thor
|
6
|
+
namespace :account
|
7
|
+
|
8
|
+
desc 'assignments', 'This command will list all of your assignments that are currently active.'
|
9
|
+
method_option :help, aliases: '-h', type: :boolean,
|
10
|
+
desc: 'Display usage information'
|
11
|
+
method_option :all, aliases: '-a', type: :boolean, default: false,
|
12
|
+
desc: 'If you want to read all assignments you have to provide this flag'
|
13
|
+
def assignments(*)
|
14
|
+
if options[:help]
|
15
|
+
invoke :help, ['assignments']
|
16
|
+
else
|
17
|
+
require_relative 'account/assignments'
|
18
|
+
Sfctl::Commands::Account::Assignments.new(options).execute
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'info', 'This will read your profile data and give you an overview of your account.'
|
23
|
+
method_option :help, aliases: '-h', type: :boolean
|
24
|
+
def info(*)
|
25
|
+
if options[:help]
|
26
|
+
invoke :help, ['info']
|
27
|
+
else
|
28
|
+
require_relative 'account/info'
|
29
|
+
Sfctl::Commands::Account::Info.new(options).execute
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'pastel'
|
2
|
+
require 'tty-table'
|
3
|
+
require_relative '../../command'
|
4
|
+
require_relative '../../starfish'
|
5
|
+
|
6
|
+
module Sfctl
|
7
|
+
module Commands
|
8
|
+
class Account
|
9
|
+
class Assignments < Sfctl::Command
|
10
|
+
def initialize(options)
|
11
|
+
@options = options
|
12
|
+
@pastel = Pastel.new(enabled: !@options['no-color'])
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(output: $stdout)
|
16
|
+
return unless config_present?(output)
|
17
|
+
|
18
|
+
success, data = Starfish.account_assignments(@options['starfish-host'], @options['all'], access_token)
|
19
|
+
|
20
|
+
unless success
|
21
|
+
output.puts @pastel.red('Something went wrong. Unable to fetch assignments')
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
print_assignments(data['assignments'], output)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def rows(assignment)
|
31
|
+
[[
|
32
|
+
<<~HEREDOC
|
33
|
+
Service: #{assignment['service']}
|
34
|
+
Start: #{assignment['start_date']}
|
35
|
+
End: #{assignment['end_date']}
|
36
|
+
Budget: #{assignment['budget']} #{assignment['unit']}
|
37
|
+
HEREDOC
|
38
|
+
]]
|
39
|
+
end
|
40
|
+
|
41
|
+
def print_assignments(assignments, output)
|
42
|
+
assignments.each do |assignment|
|
43
|
+
header = ["Assignment: #{assignment['name']}"]
|
44
|
+
table = ::TTY::Table.new header: header, rows: rows(assignment)
|
45
|
+
output.print table.render(:unicode, padding: [0, 1], multiline: true)
|
46
|
+
output.puts
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'pastel'
|
2
|
+
require 'tty-table'
|
3
|
+
require_relative '../../command'
|
4
|
+
require_relative '../../starfish'
|
5
|
+
|
6
|
+
module Sfctl
|
7
|
+
module Commands
|
8
|
+
class Account
|
9
|
+
class Info < Sfctl::Command
|
10
|
+
def initialize(options)
|
11
|
+
@options = options
|
12
|
+
@pastel = Pastel.new(enabled: !@options['no-color'])
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(output: $stdout)
|
16
|
+
return unless config_present?(output)
|
17
|
+
|
18
|
+
success, info = Starfish.account_info(@options['starfish-host'], access_token)
|
19
|
+
|
20
|
+
unless success
|
21
|
+
output.puts @pastel.red('Something went wrong. Unable to fetch account info')
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
print_table(info, output)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def print_table(info, output)
|
31
|
+
header = info.keys
|
32
|
+
rows = [info.values]
|
33
|
+
table = ::TTY::Table.new header: header, rows: rows
|
34
|
+
output.print table.render(:unicode, padding: [0, 1])
|
35
|
+
output.puts
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Sfctl
|
4
|
+
module Commands
|
5
|
+
class Auth < Thor
|
6
|
+
namespace :auth
|
7
|
+
|
8
|
+
desc 'bye', 'Log out by either removing the config file.'
|
9
|
+
method_option :help, aliases: '-h', type: :boolean, desc: '...'
|
10
|
+
def bye(*)
|
11
|
+
if options[:help]
|
12
|
+
invoke :help, ['bye']
|
13
|
+
else
|
14
|
+
require_relative 'auth/bye'
|
15
|
+
Sfctl::Commands::Auth::Bye.new(options).execute
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'init [TOKEN]', 'Authenticate with Starfish.team'
|
20
|
+
long_desc <<~HEREDOC
|
21
|
+
Before you can use sfctl, you need to authenticate with Starfish.team by providing an access token,
|
22
|
+
which can be created on the profile page of your account.
|
23
|
+
HEREDOC
|
24
|
+
method_option :help, aliases: '-h', type: :boolean
|
25
|
+
def init(access_token)
|
26
|
+
if options[:help]
|
27
|
+
invoke :help, ['init']
|
28
|
+
else
|
29
|
+
require_relative 'auth/init'
|
30
|
+
Sfctl::Commands::Auth::Init.new(access_token, options).execute
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../command'
|
4
|
+
require 'tty-prompt'
|
5
|
+
require 'tty-file'
|
6
|
+
|
7
|
+
module Sfctl
|
8
|
+
module Commands
|
9
|
+
class Auth
|
10
|
+
class Bye < Sfctl::Command
|
11
|
+
def initialize(*); end
|
12
|
+
|
13
|
+
def execute(*)
|
14
|
+
prompt = ::TTY::Prompt.new
|
15
|
+
reset_config! if prompt.yes?('Are you sure?')
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def reset_config!
|
21
|
+
::TTY::File.remove_file CONFIG_PATH
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|