sfctl 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../command'
|
4
|
+
require_relative '../../starfish'
|
5
|
+
require 'pastel'
|
6
|
+
require 'tty-spinner'
|
7
|
+
|
8
|
+
module Sfctl
|
9
|
+
module Commands
|
10
|
+
class Auth
|
11
|
+
class Init < Sfctl::Command
|
12
|
+
def initialize(access_token, options)
|
13
|
+
@access_token = access_token
|
14
|
+
@options = options
|
15
|
+
|
16
|
+
@pastel = Pastel.new(enabled: !@options['no-color'])
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute(output: $stdout)
|
20
|
+
spinner = ::TTY::Spinner.new('[:spinner] Checking token ...')
|
21
|
+
spinner.auto_spin
|
22
|
+
token_valid? ? update_config!(spinner, output) : render_error(spinner, output)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def token_valid?
|
28
|
+
Starfish.check_authorization(@options['starfish-host'], @access_token)
|
29
|
+
end
|
30
|
+
|
31
|
+
def token_accepted_message
|
32
|
+
@pastel.green('Credentials are accepted.')
|
33
|
+
end
|
34
|
+
|
35
|
+
def wrong_token_message
|
36
|
+
@pastel.red('Token is not accepted, please make sure you copy and paste it correctly.')
|
37
|
+
end
|
38
|
+
|
39
|
+
def update_config!(spinner, output)
|
40
|
+
config.set :access_token, value: @access_token
|
41
|
+
save_config!
|
42
|
+
spinner.success
|
43
|
+
output.puts token_accepted_message
|
44
|
+
end
|
45
|
+
|
46
|
+
def render_error(spinner, output)
|
47
|
+
spinner.error
|
48
|
+
output.puts wrong_token_message
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Sfctl
|
4
|
+
module Commands
|
5
|
+
class Time < Thor
|
6
|
+
namespace :time
|
7
|
+
|
8
|
+
desc 'sync', 'Synchronize data with providers.'
|
9
|
+
method_option :help, aliases: '-h', type: :boolean,
|
10
|
+
desc: 'Display usage information'
|
11
|
+
method_option :dry_run, aliases: '-dry-run', type: :boolean, default: false,
|
12
|
+
desc: 'Check the data first respectively prevent data from being overwritten'
|
13
|
+
method_option :touchy, aliases: '-touchy', type: :boolean, default: false,
|
14
|
+
desc: 'The synchronizsation will be skipped if there is preexisting data.'
|
15
|
+
long_desc <<~HEREDOC
|
16
|
+
It will gets for each assignment the next reporting segment from starfish.team
|
17
|
+
and loads the corresponding time reports from the provider.
|
18
|
+
HEREDOC
|
19
|
+
def sync(*)
|
20
|
+
if options[:help]
|
21
|
+
invoke :help, ['sync']
|
22
|
+
else
|
23
|
+
require_relative 'time/sync'
|
24
|
+
Sfctl::Commands::Time::Sync.new(options).execute
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'init',
|
29
|
+
'You can use the following command to create a .sflink file that will store your project configuration.'
|
30
|
+
long_desc <<~HEREDOC
|
31
|
+
You can use the following command to create a .sflink file that will store your project configuration.\n
|
32
|
+
Although sensitive data is stored in the main .sfctl directory
|
33
|
+
we'd like to recommend to not add the .sflink file to your version control system.
|
34
|
+
HEREDOC
|
35
|
+
method_option :help, aliases: '-h', type: :boolean
|
36
|
+
def init(*)
|
37
|
+
if options[:help]
|
38
|
+
invoke :help, ['init']
|
39
|
+
else
|
40
|
+
require_relative 'time/init'
|
41
|
+
Sfctl::Commands::Time::Init.new(options).execute
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
require_relative 'time/providers'
|
46
|
+
register Sfctl::Commands::Time::Providers, 'providers', 'providers [SUBCOMMAND]', 'Manage providers.'
|
47
|
+
|
48
|
+
require_relative 'time/connections'
|
49
|
+
register Sfctl::Commands::Time::Connections, 'connections', 'connections [SUBCOMMAND]', 'Manage connections.'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Sfctl
|
4
|
+
module Commands
|
5
|
+
class Time
|
6
|
+
class Connections < Thor
|
7
|
+
namespace :connections
|
8
|
+
|
9
|
+
desc 'add', 'This command will add a connection between a provider and an assignment.'
|
10
|
+
method_option :help, aliases: '-h', type: :boolean
|
11
|
+
def add(*)
|
12
|
+
if options[:help]
|
13
|
+
invoke :help, ['add']
|
14
|
+
else
|
15
|
+
require_relative 'connections/add'
|
16
|
+
Sfctl::Commands::Time::Connections::Add.new(options).execute
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'get', 'List all known connections in that project.'
|
21
|
+
method_option :help, aliases: '-h', type: :boolean
|
22
|
+
def get(*)
|
23
|
+
if options[:help]
|
24
|
+
invoke :help, ['get']
|
25
|
+
else
|
26
|
+
require_relative 'connections/get'
|
27
|
+
Sfctl::Commands::Time::Connections::Get.new(options).execute
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'pastel'
|
2
|
+
require 'tty-prompt'
|
3
|
+
require_relative '../../../command'
|
4
|
+
require_relative '../../../starfish'
|
5
|
+
|
6
|
+
module Sfctl
|
7
|
+
module Commands
|
8
|
+
class Time
|
9
|
+
class Connections
|
10
|
+
class Add < Sfctl::Command
|
11
|
+
def initialize(options)
|
12
|
+
@options = options
|
13
|
+
@pastel = Pastel.new(enabled: !@options['no-color'])
|
14
|
+
@prompt = ::TTY::Prompt.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute(output: $stdout) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
18
|
+
return if !config_present?(output) || !link_config_present?(output)
|
19
|
+
|
20
|
+
ltoken = access_token
|
21
|
+
config.delete(:access_token)
|
22
|
+
success, data = Starfish.account_assignments(@options['starfish-host'], @options['all'], ltoken)
|
23
|
+
unless success
|
24
|
+
output.puts @pastel.red('Something went wrong. Unable to fetch assignments')
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
assignments = filter_assignments(data['assignments'])
|
29
|
+
if assignments.length.zero?
|
30
|
+
output.puts @pastel.yellow('All assignments already added.')
|
31
|
+
return
|
32
|
+
end
|
33
|
+
|
34
|
+
provider = @prompt.select('Select provider:', PROVIDERS_LIST)
|
35
|
+
|
36
|
+
assignment_name = select_assignment(assignments)
|
37
|
+
|
38
|
+
case provider
|
39
|
+
when TOGGL_PROVIDER
|
40
|
+
setup_toggl_connection!(assignment_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
save_link_config!
|
44
|
+
|
45
|
+
output.puts @pastel.green('Connection successfully added.')
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def select_assignment(assignments)
|
51
|
+
@prompt.select('Select assignment:') do |menu|
|
52
|
+
assignments.each.with_index do |asmnt, i|
|
53
|
+
menu.choice name: "#{i + 1}. #{asmnt['name']} / #{asmnt['service']}", value: asmnt['name']
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def filter_assignments(list)
|
59
|
+
return list if config.fetch(:connections).nil?
|
60
|
+
|
61
|
+
added_assignments_name = config.fetch(:connections).keys
|
62
|
+
list.delete_if { |h| added_assignments_name.include?(h['name']) }
|
63
|
+
list
|
64
|
+
end
|
65
|
+
|
66
|
+
def setup_toggl_connection!(assignment_name)
|
67
|
+
workspace_id = @prompt.ask('Workspace ID (required):', required: true)
|
68
|
+
project_ids = @prompt.ask('Project IDs (required / comma separated):', required: true)
|
69
|
+
task_ids = @prompt.ask('Task IDs (optional / comma separated):') || ''
|
70
|
+
billable = @prompt.select('Billable? (required)', %w[yes no both])
|
71
|
+
rounding = @prompt.select('Rounding? (required)', %w[on off])
|
72
|
+
|
73
|
+
config.set("connections.#{assignment_name}.provider", value: TOGGL_PROVIDER)
|
74
|
+
config.set("connections.#{assignment_name}.workspace_id", value: workspace_id)
|
75
|
+
config.set("connections.#{assignment_name}.project_ids", value: project_ids)
|
76
|
+
config.set("connections.#{assignment_name}.task_ids", value: task_ids)
|
77
|
+
config.set("connections.#{assignment_name}.billable", value: billable)
|
78
|
+
config.set("connections.#{assignment_name}.rounding", value: rounding)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'pastel'
|
2
|
+
require_relative '../../../command'
|
3
|
+
|
4
|
+
module Sfctl
|
5
|
+
module Commands
|
6
|
+
class Time
|
7
|
+
class Connections
|
8
|
+
class Get < Sfctl::Command
|
9
|
+
def initialize(options)
|
10
|
+
@options = options
|
11
|
+
@pastel = Pastel.new(enabled: !@options['no-color'])
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(output: $stdout)
|
15
|
+
read_link_config
|
16
|
+
|
17
|
+
if config.fetch(:connections).nil?
|
18
|
+
output.puts @pastel.yellow('You have no connections. Please add them before continue.')
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
print_connections(output)
|
23
|
+
rescue TTY::Config::ReadError
|
24
|
+
error_message = 'Please initialize time before continue and ensure that your account authenticated.'
|
25
|
+
output.puts @pastel.yellow(error_message)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def print_connections(output)
|
31
|
+
config.fetch(:connections).each_key do |assignment_name|
|
32
|
+
case config.fetch(:connections, assignment_name, :provider)
|
33
|
+
when TOGGL_PROVIDER
|
34
|
+
print_toggl_connection!(output, assignment_name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def print_toggl_connection!(output, assignment_name)
|
40
|
+
output.puts "Connection: #{assignment_name}"
|
41
|
+
output.puts " provider: #{TOGGL_PROVIDER}"
|
42
|
+
output.puts " workspace_id: #{config.fetch(:connections, assignment_name, :workspace_id)}"
|
43
|
+
output.puts " project_ids: #{config.fetch(:connections, assignment_name, :project_ids)}"
|
44
|
+
output.puts " task_ids: #{config.fetch(:connections, assignment_name, :task_ids)}"
|
45
|
+
output.puts " billable: #{config.fetch(:connections, assignment_name, :billable)}"
|
46
|
+
output.puts " rounding: #{config.fetch(:connections, assignment_name, :rounding)}"
|
47
|
+
output.puts
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../../command'
|
2
|
+
|
3
|
+
module Sfctl
|
4
|
+
module Commands
|
5
|
+
class Time
|
6
|
+
class Init < Sfctl::Command
|
7
|
+
def initialize(options)
|
8
|
+
@options = options
|
9
|
+
|
10
|
+
@pastel = Pastel.new(enabled: !@options['no-color'])
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(output: $stdout)
|
14
|
+
read_link_config
|
15
|
+
output.puts @pastel.yellow('.sflink is already created.')
|
16
|
+
rescue ::TTY::Config::ReadError
|
17
|
+
save_link_config!
|
18
|
+
output.puts @pastel.green('.sflink successfully created.')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Sfctl
|
4
|
+
module Commands
|
5
|
+
class Time
|
6
|
+
class Providers < Thor
|
7
|
+
namespace :providers
|
8
|
+
|
9
|
+
desc 'set', 'Set the configuration required for the provider to authenticate a call to their API.'
|
10
|
+
method_option :help, aliases: '-h', type: :boolean
|
11
|
+
def set(*)
|
12
|
+
if options[:help]
|
13
|
+
invoke :help, ['set']
|
14
|
+
else
|
15
|
+
require_relative 'providers/set'
|
16
|
+
Sfctl::Commands::Time::Providers::Set.new(options).execute
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'unset', 'Unset the configuration of a provider.'
|
21
|
+
method_option :help, aliases: '-h', type: :boolean
|
22
|
+
def unset(*)
|
23
|
+
if options[:help]
|
24
|
+
invoke :help, ['unset']
|
25
|
+
else
|
26
|
+
require_relative 'providers/unset'
|
27
|
+
Sfctl::Commands::Time::Providers::Unset.new(options).execute
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'get', 'Read which providers are configured on your system.'
|
32
|
+
method_option :help, aliases: '-h', type: :boolean
|
33
|
+
def get(*)
|
34
|
+
if options[:help]
|
35
|
+
invoke :help, ['get']
|
36
|
+
else
|
37
|
+
require_relative 'providers/get'
|
38
|
+
Sfctl::Commands::Time::Providers::Get.new(options).execute
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'pastel'
|
2
|
+
require_relative '../../../command'
|
3
|
+
|
4
|
+
module Sfctl
|
5
|
+
module Commands
|
6
|
+
class Time
|
7
|
+
class Providers
|
8
|
+
class Get < Sfctl::Command
|
9
|
+
def initialize(options)
|
10
|
+
@options = options
|
11
|
+
@pastel = Pastel.new(enabled: !@options['no-color'])
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(output: $stdout)
|
15
|
+
read_link_config
|
16
|
+
|
17
|
+
PROVIDERS_LIST.each do |provider|
|
18
|
+
read(provider, output)
|
19
|
+
end
|
20
|
+
rescue TTY::Config::ReadError
|
21
|
+
output.puts @pastel.yellow('Please initialize time before continue.')
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def read(provider, output)
|
27
|
+
info = config.fetch("providers.#{provider}")
|
28
|
+
if info.nil?
|
29
|
+
output.puts @pastel.yellow("Provider #{provider} is not set.")
|
30
|
+
else
|
31
|
+
output.puts "Provider: #{@pastel.cyan(provider)}"
|
32
|
+
info.each_key do |k|
|
33
|
+
output.puts " #{k.upcase}: #{info[k]}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'pastel'
|
2
|
+
require 'tty-prompt'
|
3
|
+
require_relative '../../../command'
|
4
|
+
|
5
|
+
module Sfctl
|
6
|
+
module Commands
|
7
|
+
class Time
|
8
|
+
class Providers
|
9
|
+
class Set < 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
|
+
read_link_config
|
17
|
+
|
18
|
+
prompt = ::TTY::Prompt.new
|
19
|
+
provider = prompt.select('Setting up:', PROVIDERS_LIST)
|
20
|
+
|
21
|
+
!ask_for_replace(output, prompt) && return unless config.fetch("providers.#{TOGGL_PROVIDER}").nil?
|
22
|
+
|
23
|
+
case provider
|
24
|
+
when TOGGL_PROVIDER
|
25
|
+
setup_toggl_provider!(output, prompt)
|
26
|
+
end
|
27
|
+
rescue TTY::Config::ReadError
|
28
|
+
output.puts @pastel.yellow('Please initialize time before continue.')
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def ask_for_replace(output, prompt)
|
34
|
+
output.puts @pastel.yellow('You already have a configuration for this provider.')
|
35
|
+
prompt.yes?('Do you want to replace it?')
|
36
|
+
end
|
37
|
+
|
38
|
+
def save_toggl_config!(output, access_token)
|
39
|
+
config.set("providers.#{TOGGL_PROVIDER}.access_token", value: access_token)
|
40
|
+
save_link_config!
|
41
|
+
output.puts @pastel.green('Everything saved.')
|
42
|
+
end
|
43
|
+
|
44
|
+
def setup_toggl_provider!(output, prompt)
|
45
|
+
output.puts
|
46
|
+
access_token = prompt.ask("Your access token at [#{@pastel.green(TOGGL_PROVIDER)}]:", required: true)
|
47
|
+
is_correct = prompt.yes?('Is that information correct?')
|
48
|
+
if is_correct
|
49
|
+
save_toggl_config!(output, access_token)
|
50
|
+
else
|
51
|
+
setup_toggl_provider!(output, prompt)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|