flo 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -6
- data/bin/flo +12 -0
- data/flo.gemspec +17 -12
- data/lib/flo/cli.rb +76 -0
- data/lib/flo/command.rb +22 -1
- data/lib/flo/command_collection.rb +16 -6
- data/lib/flo/config.rb +32 -3
- data/lib/flo/cred_store/creds.rb +27 -0
- data/lib/flo/cred_store/pw_protected_store.rb +59 -0
- data/lib/flo/cred_store/yaml_store.rb +36 -0
- data/lib/flo/error.rb +9 -0
- data/lib/flo/provider/base.rb +62 -0
- data/lib/flo/provider/developer.rb +5 -7
- data/lib/flo/runner.rb +24 -9
- data/lib/flo/task.rb +28 -10
- data/lib/flo/version.rb +1 -1
- data/lib/flo.rb +3 -2
- data/test/fixtures/basic_setup.rb +7 -2
- data/test/fixtures/cred_example.yml +3 -0
- data/test/flo/cli_test.rb +115 -0
- data/test/flo/command_collection_test.rb +0 -16
- data/test/flo/command_test.rb +12 -0
- data/test/flo/config_test.rb +28 -1
- data/test/flo/cred_store/creds_test.rb +29 -0
- data/test/flo/cred_store/pw_protected_store_test.rb +91 -0
- data/test/flo/cred_store/yaml_store_test.rb +34 -0
- data/test/flo/provider/base_test.rb +51 -0
- data/test/flo/runner_integration_test.rb +8 -2
- data/test/flo/runner_test.rb +13 -16
- data/test/flo/task_test.rb +15 -5
- data/test/minitest_helper.rb +1 -0
- metadata +79 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c4b34788a7d80192abd6ec7d302ebc9b0e44615
|
4
|
+
data.tar.gz: 26f726d4839283d43adc1a4fafd9a47464338b7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8b474dfc8872e4a04cd236c51837dda2668ef9f7d75bb405935f5cfd5998adcfe6068b09a38010a4f7b06d1cd5a37c99e3b67c246401a628836964a59d77a26
|
7
|
+
data.tar.gz: 38c4d9b143a55b62fbb9c71f86d53d7b62e19fc485c343e2b61cc67f640c54bb3fba302e2a42d5fca4161480d74c5a2170e852dd946417a69f5da389eb51d939
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Flo
|
2
|
-
[![Code Climate](https://codeclimate.com/github/
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/flo.svg)](https://badge.fury.io/rb/flo) [![Code Climate](https://codeclimate.com/github/salesforce/flo/badges/gpa.svg)](https://codeclimate.com/github/salesforce/flo) [![Test Coverage](https://codeclimate.com/github/salesforce/flo/badges/coverage.svg)](https://codeclimate.com/github/salesforce/flo/coverage) [![Build Status](https://semaphoreci.com/api/v1/justinpowers/flo/branches/master/shields_badge.svg)](https://semaphoreci.com/justinpowers/flo) [![Inline docs](http://inch-ci.org/github/salesforce/flo.svg?branch=master)](http://inch-ci.org/github/salesforce/flo)
|
3
3
|
|
4
4
|
|
5
5
|
Flo is a local workflow automation tool that helps you get things done. This gem contains the core functionality for Flo, plugins for interacting with various systems can be found in separate provider gems.
|
@@ -23,14 +23,23 @@ $ gem install flo
|
|
23
23
|
```
|
24
24
|
|
25
25
|
## Documentation
|
26
|
-
|
26
|
+
http://www.rubydoc.info/github/salesforce/flo/
|
27
27
|
|
28
28
|
## Usage
|
29
29
|
|
30
30
|
### Command line usage
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
If you have a `.flo` configuration file (See [.flo configuration file](#flo-configuration-file)), in your current working directory and/or your home directory, flo will parse your configuration file(s) and generate CLI commands for you. To list out the possible commands, use
|
33
|
+
|
34
|
+
```bash
|
35
|
+
flo help
|
36
|
+
```
|
37
|
+
|
38
|
+
You can also see the usage and options for specific commands:
|
39
|
+
|
40
|
+
```bash
|
41
|
+
flo help <command>
|
42
|
+
```
|
34
43
|
|
35
44
|
### Ruby script usage
|
36
45
|
|
@@ -45,7 +54,7 @@ runner = Flo::Runner.new
|
|
45
54
|
runner.load_config_file(File.join(__dir__,'.flo'))
|
46
55
|
|
47
56
|
# Run the something:useful command defined in the .flo file
|
48
|
-
response = runner.execute(
|
57
|
+
response = runner.execute('something:useful', id: '123')
|
49
58
|
```
|
50
59
|
|
51
60
|
|
@@ -73,7 +82,7 @@ You can register any number of commands. Commands are namespaced to make it eas
|
|
73
82
|
# Registers a command for starting a feature - feature:start. This command has
|
74
83
|
# one required argument: 'feature_name'. Note that in order for this to work,
|
75
84
|
# you will need to declare the git_flo provider in the config section.
|
76
|
-
register_command(
|
85
|
+
register_command('feature:start') do |feature_name: nil|
|
77
86
|
|
78
87
|
# During command execution, perform the :check_out_or_create_branch method on
|
79
88
|
# the git_flo provider, passing in the :from and :name arguments
|
@@ -88,6 +97,7 @@ end
|
|
88
97
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
89
98
|
4. Push to the branch (`git push origin my-new-feature`)
|
90
99
|
5. Create new Pull Request
|
100
|
+
6. If this is your first contribution, you will need Accept the Contributor License Agreement. You can follow the link in the pull request 'checks' section to do so.
|
91
101
|
|
92
102
|
## License
|
93
103
|
|
data/bin/flo
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright © 2017, Salesforce.com, Inc.
|
4
|
+
# All Rights Reserved.
|
5
|
+
# Licensed under the BSD 3-Clause license.
|
6
|
+
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
9
|
+
require 'flo'
|
10
|
+
require 'flo/cli'
|
11
|
+
|
12
|
+
Flo::Cli.new.call(ARGV)
|
data/flo.gemspec
CHANGED
@@ -9,24 +9,29 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
9
9
|
require 'flo/version'
|
10
10
|
|
11
11
|
Gem::Specification.new do |spec|
|
12
|
-
spec.name =
|
12
|
+
spec.name = 'flo'
|
13
13
|
spec.version = Flo::VERSION
|
14
|
-
spec.authors = [
|
15
|
-
spec.email = [
|
14
|
+
spec.authors = ['Justin Powers']
|
15
|
+
spec.email = ['justinspowers@gmail.com']
|
16
16
|
spec.summary = %q{Simple developer workflow automation}
|
17
17
|
spec.description = %q{Flo is a local workflow automation tool that helps you get things done. This gem contains the core functionality for Flo, plugins for interacting with various systems can be found in separate provider gems.}
|
18
|
-
spec.homepage =
|
19
|
-
spec.license =
|
18
|
+
spec.homepage = 'https://github.com/salesforce/flo'
|
19
|
+
spec.license = 'BSD-3-Clause'
|
20
20
|
|
21
|
+
spec.executables << 'flo'
|
21
22
|
spec.files = `git ls-files -z`.split("\x0")
|
22
23
|
spec.test_files = spec.files.grep(%r{^test/})
|
23
|
-
spec.require_paths = [
|
24
|
+
spec.require_paths = ['lib']
|
24
25
|
|
25
|
-
spec.add_dependency
|
26
|
+
spec.add_dependency 'cleanroom'
|
27
|
+
spec.add_dependency 'gpgme'
|
28
|
+
spec.add_dependency 'cri'
|
26
29
|
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
29
|
-
spec.add_development_dependency
|
30
|
-
spec.add_development_dependency
|
31
|
-
spec.add_development_dependency
|
30
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
31
|
+
spec.add_development_dependency 'rake'
|
32
|
+
spec.add_development_dependency 'minitest'
|
33
|
+
spec.add_development_dependency 'minitest-proveit'
|
34
|
+
spec.add_development_dependency 'pry'
|
35
|
+
spec.add_development_dependency 'simplecov'
|
36
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
32
37
|
end
|
data/lib/flo/cli.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Copyright © 2017, Salesforce.com, Inc.
|
2
|
+
# All Rights Reserved.
|
3
|
+
# Licensed under the BSD 3-Clause license.
|
4
|
+
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
5
|
+
|
6
|
+
require 'cri'
|
7
|
+
|
8
|
+
module Flo
|
9
|
+
|
10
|
+
# Builds the command line parser and executes Flo::Runner using the commands supplied.
|
11
|
+
# In general you should be able to create a new instance and then pass in ARGV directly
|
12
|
+
# into {#call}.
|
13
|
+
class Cli
|
14
|
+
|
15
|
+
# Creates a new CLI runner instance.
|
16
|
+
# @option opts [Class] runner_class (Runner) Class of the runner object dependency
|
17
|
+
def initialize(opts={})
|
18
|
+
@runner_class = opts[:runner_class] || Runner
|
19
|
+
end
|
20
|
+
|
21
|
+
# Runs a command using args directly from ARGV.
|
22
|
+
# @param argv [Array<String>] An array of strings. Typically you would pass in ARGV directly.
|
23
|
+
# The first value should be the name of the command
|
24
|
+
#
|
25
|
+
def call(argv)
|
26
|
+
generate_commands.run(argv)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :runner_class
|
32
|
+
|
33
|
+
def generate_commands
|
34
|
+
flo_runner = runner_class.new
|
35
|
+
flo_runner.load_default_config_files
|
36
|
+
|
37
|
+
flo_runner.commands.each do |cmd_name, cmd|
|
38
|
+
|
39
|
+
main_command.define_command do
|
40
|
+
options = cmd[:command].required_parameters
|
41
|
+
|
42
|
+
unless cmd[:command].optional_parameters.empty?
|
43
|
+
options << '[options]'
|
44
|
+
|
45
|
+
cmd[:command].optional_parameters.each do |param|
|
46
|
+
optional(nil, param, param.to_s.tr('_', ' '))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
name(cmd_name)
|
51
|
+
usage("#{cmd_name} #{options.join(' ')}")
|
52
|
+
summary(cmd[:summary])
|
53
|
+
description(cmd[:description])
|
54
|
+
|
55
|
+
run do |opts, arguments, command|
|
56
|
+
flo_runner.execute(command.name, arguments.dup.push(opts))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
main_command
|
62
|
+
end
|
63
|
+
|
64
|
+
def main_command
|
65
|
+
@main_command ||= Cri::Command.new_basic_root.modify do
|
66
|
+
name 'flo'
|
67
|
+
usage 'flo [command]'
|
68
|
+
summary 'Local workflow automation'
|
69
|
+
description 'Use `flo help [command]` for usage on individual commands'
|
70
|
+
|
71
|
+
default_subcommand 'help'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
data/lib/flo/command.rb
CHANGED
@@ -87,11 +87,32 @@ module Flo
|
|
87
87
|
end.last
|
88
88
|
end
|
89
89
|
|
90
|
+
# Returns a list of any required parameters
|
91
|
+
#
|
92
|
+
# Required parameters are generated automatically by inspecting the required
|
93
|
+
# parameters for the definition lambda
|
94
|
+
# @return [Array<Symbol>] An array of symbols representing required parameters
|
95
|
+
#
|
96
|
+
def required_parameters
|
97
|
+
definition_lambda.parameters.select { |key,_value| key == :req }.map { |_key,value| value }
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
# Returns a list of any optional parameters
|
102
|
+
#
|
103
|
+
# Optional parameters are generated automatically by inspecting the optional
|
104
|
+
# parameters for the definition lambda
|
105
|
+
# @return [Array<Symbol>] An array of symbols representing optional parameters
|
106
|
+
#
|
107
|
+
def optional_parameters
|
108
|
+
definition_lambda.parameters.select { |key,_value| key == :key }.map { |_key,value| value }
|
109
|
+
end
|
110
|
+
|
90
111
|
private
|
91
112
|
|
92
113
|
attr_reader :tasks, :definition_lambda, :providers, :state_class
|
93
114
|
|
94
|
-
def evaluate_command_definition(
|
115
|
+
def evaluate_command_definition(args)
|
95
116
|
cleanroom.instance_exec(*args, &definition_lambda)
|
96
117
|
end
|
97
118
|
|
@@ -3,22 +3,32 @@
|
|
3
3
|
# Licensed under the BSD 3-Clause license.
|
4
4
|
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
5
5
|
|
6
|
+
require 'forwardable'
|
7
|
+
require 'flo/error'
|
8
|
+
|
6
9
|
module Flo
|
7
|
-
|
10
|
+
|
11
|
+
CommandNotDefinedError = Class.new(Flo::Error)
|
12
|
+
|
13
|
+
# A collection of commands. Behaves like a Hash, except that fetching a key that does
|
14
|
+
# not exist raises an error
|
8
15
|
class CommandCollection
|
16
|
+
extend Forwardable
|
17
|
+
def_delegators :@commands, :each, :[]=
|
9
18
|
|
10
19
|
def initialize
|
11
20
|
@commands = {}
|
12
21
|
end
|
13
22
|
|
14
|
-
|
23
|
+
# Returns the value corresponding to the key. Identical to accessing Hash values, except
|
24
|
+
# that fetching a value that does not exist raises an error
|
25
|
+
# @param key [String] Name of the command
|
26
|
+
# @raises Flo::CommandNotDefinedError if the command does not exist
|
27
|
+
#
|
28
|
+
def [](key)
|
15
29
|
raise CommandNotDefinedError.new("#{key} command is not defined") unless @commands.has_key?(key)
|
16
30
|
@commands[key]
|
17
31
|
end
|
18
32
|
|
19
|
-
def []=(*key, command)
|
20
|
-
@commands[key] = command
|
21
|
-
end
|
22
|
-
|
23
33
|
end
|
24
34
|
end
|
data/lib/flo/config.rb
CHANGED
@@ -3,14 +3,30 @@
|
|
3
3
|
# Licensed under the BSD 3-Clause license.
|
4
4
|
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
5
5
|
|
6
|
+
require 'flo/cred_store/yaml_store'
|
7
|
+
|
6
8
|
module Flo
|
7
|
-
MissingRequireError = Class.new(
|
9
|
+
MissingRequireError = Class.new(Flo::Error)
|
8
10
|
|
9
11
|
# Instantiates and stores providers for use in command definitions
|
10
12
|
#
|
11
13
|
# @attr_reader providers [Hash] Hash of provider instances
|
12
14
|
#
|
13
15
|
class Config
|
16
|
+
|
17
|
+
# A placeholder for credentials that can be evaluated at a later time
|
18
|
+
class LazyCreds
|
19
|
+
|
20
|
+
# Returns a lambda that takes a credential store for a specific provider as an argument.
|
21
|
+
# This prevents providers from accessing the credentials for another provider
|
22
|
+
# @param key [Symbol,String]
|
23
|
+
# @return [lambda]
|
24
|
+
def [](key)
|
25
|
+
lambda { |cred_store| cred_store[key] }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_writer :cred_store
|
14
30
|
attr_reader :providers
|
15
31
|
|
16
32
|
def initialize
|
@@ -24,7 +40,20 @@ module Flo
|
|
24
40
|
# accepts a block
|
25
41
|
#
|
26
42
|
def provider(provider_sym, options={}, &blk)
|
27
|
-
|
43
|
+
provider = provider_class(provider_sym).new(options, &blk)
|
44
|
+
provider.cred_store = cred_store.credentials_for(provider_sym)
|
45
|
+
@providers[provider_sym] = provider
|
46
|
+
end
|
47
|
+
|
48
|
+
def cred_store
|
49
|
+
@cred_store ||= Flo::CredStore::YamlStore.new
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns an object that responds to [], but instead of returning a value, returns
|
53
|
+
# a lambda that can be evaluated later in the context of the credentials for the provider
|
54
|
+
# @return [LazyCred]
|
55
|
+
def creds
|
56
|
+
LazyCreds.new
|
28
57
|
end
|
29
58
|
|
30
59
|
private
|
@@ -33,7 +62,7 @@ module Flo
|
|
33
62
|
klass = camel_case(provider_sym.to_s)
|
34
63
|
klass_name = "Flo::Provider::#{klass}"
|
35
64
|
Object.const_get(klass_name)
|
36
|
-
rescue NameError
|
65
|
+
rescue NameError
|
37
66
|
raise MissingRequireError.new("#{klass_name} is not loaded. Please require the library before use")
|
38
67
|
end
|
39
68
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Copyright © 2017, Salesforce.com, Inc.
|
2
|
+
# All Rights Reserved.
|
3
|
+
# Licensed under the BSD 3-Clause license.
|
4
|
+
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
5
|
+
|
6
|
+
module Flo
|
7
|
+
MissingCredentialError = Class.new(Flo::Error)
|
8
|
+
|
9
|
+
module CredStore
|
10
|
+
class Creds
|
11
|
+
|
12
|
+
def initialize(credentials)
|
13
|
+
@credentials = credentials
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the credential referenced by the key provided
|
17
|
+
# @param key [Symbol, String]
|
18
|
+
# @raises Flo::MissingCredentialError if key does not exist
|
19
|
+
#
|
20
|
+
def [](key)
|
21
|
+
raise Flo::MissingCredentialError unless @credentials.key?(key)
|
22
|
+
|
23
|
+
@credentials[key]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright © 2017, Salesforce.com, Inc.
|
2
|
+
# All Rights Reserved.
|
3
|
+
# Licensed under the BSD 3-Clause license.
|
4
|
+
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
5
|
+
|
6
|
+
module Flo
|
7
|
+
module CredStore
|
8
|
+
class PwProtectedStore
|
9
|
+
|
10
|
+
attr_writer :cred_file
|
11
|
+
|
12
|
+
def initialize(opts={})
|
13
|
+
@password = opts[:password] # If nil, should prompt interactively
|
14
|
+
@cred_file_location = opts[:cred_file_location]
|
15
|
+
end
|
16
|
+
|
17
|
+
# Decrypts the credentials file and returns the credentials for the requested provider
|
18
|
+
# @param provider_sym [Symbol]
|
19
|
+
# @return [Flo::CredStore::Creds]
|
20
|
+
#
|
21
|
+
def credentials_for(provider_sym)
|
22
|
+
Flo::CredStore::Creds.new(full_credentials_hash[provider_sym])
|
23
|
+
end
|
24
|
+
|
25
|
+
# Convenience method for producing an encrypted version of a file. This only returns
|
26
|
+
# the encrypted version as a string, you will have to save it yourself if desired
|
27
|
+
# @param file_location [String]
|
28
|
+
# @return [String]
|
29
|
+
def encrypt_file(file_location)
|
30
|
+
crypto.encrypt(File.open(file_location)).to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def cred_file
|
34
|
+
@cred_file ||= File.new(@cred_file_location || File.join(Dir.home, '.flo_creds.yml.gpg'))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Remove password from inspect output
|
38
|
+
def inspect
|
39
|
+
"#<Flo::CredStore::PwProtectedStore:#{object_id} @cred_file=#{cred_file.inspect}>"
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :password
|
45
|
+
|
46
|
+
def decrypted_file
|
47
|
+
crypto.decrypt(cred_file)
|
48
|
+
end
|
49
|
+
|
50
|
+
def full_credentials_hash
|
51
|
+
@full_credentials_hash ||= YAML.load(decrypted_file.read) || {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def crypto
|
55
|
+
GPGME::Crypto.new(password: @password, symmetric: true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyright © 2017, Salesforce.com, Inc.
|
2
|
+
# All Rights Reserved.
|
3
|
+
# Licensed under the BSD 3-Clause license.
|
4
|
+
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
5
|
+
|
6
|
+
require 'yaml'
|
7
|
+
require 'flo/error'
|
8
|
+
require 'flo/cred_store/creds'
|
9
|
+
|
10
|
+
module Flo
|
11
|
+
|
12
|
+
MissingProviderError = Class.new(Flo::Error)
|
13
|
+
|
14
|
+
module CredStore
|
15
|
+
class YamlStore
|
16
|
+
|
17
|
+
def initialize(location=nil)
|
18
|
+
@location = location || File.join(Dir.home, '.flo_creds.yml')
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the credentials for the requested provider
|
22
|
+
# @param provider_sym [Symbol]
|
23
|
+
# @return [Flo::CredStore::Creds]
|
24
|
+
#
|
25
|
+
def credentials_for(provider_sym)
|
26
|
+
Flo::CredStore::Creds.new(full_credentials_hash[provider_sym])
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def full_credentials_hash
|
32
|
+
@full_credentials_hash ||= YAML.load(File.read(@location)) || {}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/flo/error.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Copyright © 2017, Salesforce.com, Inc.
|
2
|
+
# All Rights Reserved.
|
3
|
+
# Licensed under the BSD 3-Clause license.
|
4
|
+
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
5
|
+
|
6
|
+
module Flo
|
7
|
+
|
8
|
+
Error = Class.new(StandardError)
|
9
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Copyright © 2017, Salesforce.com, Inc.
|
2
|
+
# All Rights Reserved.
|
3
|
+
# Licensed under the BSD 3-Clause license.
|
4
|
+
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
5
|
+
|
6
|
+
module Flo
|
7
|
+
module Provider
|
8
|
+
MissingOptionError = Class.new(StandardError)
|
9
|
+
OptionDefinition = Struct.new(:default, :required)
|
10
|
+
|
11
|
+
# @abstract Subclass and use {.option} to declare initialize options
|
12
|
+
# A base provider class that all custom providers should inherit from.
|
13
|
+
#
|
14
|
+
class Base
|
15
|
+
|
16
|
+
attr_writer :cred_store
|
17
|
+
|
18
|
+
# Creates an instance of a provider
|
19
|
+
# @param opts [Hash] Arbitrary array of options defined by the provider
|
20
|
+
# using the {.option} method
|
21
|
+
# @raise [MissingOptionError] if a required option is not provided. Error
|
22
|
+
# message will state which options are missing.
|
23
|
+
#
|
24
|
+
def initialize(opts={})
|
25
|
+
# @options = allow_whitelisted(opts, @@option_keys)
|
26
|
+
@options = {}
|
27
|
+
self.class.option_keys.each do |key, definition|
|
28
|
+
@options[key] = opts.fetch(key, definition.default)
|
29
|
+
end
|
30
|
+
|
31
|
+
missing_required = self.class.option_keys.select {|_k,v| v.required }.keys - @options.select { |_k,v| !v.nil? }.keys
|
32
|
+
unless missing_required.empty?
|
33
|
+
raise MissingOptionError.new("#{self.class.name} invoked without required options: #{missing_required.join(' ')}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Declare an option to be passed in when declaring the provider in the
|
38
|
+
# .flo file
|
39
|
+
# @param name [Symbol] The name of the option
|
40
|
+
# @param default Default value for the option if none is provided
|
41
|
+
# @option args [Boolean] :required (false) Whether the option is required.
|
42
|
+
# A MissingOptionError will be raised if a provider is instantiated
|
43
|
+
# without a required argument
|
44
|
+
#
|
45
|
+
def self.option(name, default=nil, args={})
|
46
|
+
option_keys[name] = OptionDefinition.new(default, args[:required] == true)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Hash of option definitions. Add to this hash using the {.option}
|
50
|
+
# method.
|
51
|
+
#
|
52
|
+
# @return [Hash{Symbol => OptionDefiniton}]
|
53
|
+
def self.option_keys
|
54
|
+
@option_keys ||= {}
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
attr_reader :options
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -3,16 +3,15 @@
|
|
3
3
|
# Licensed under the BSD 3-Clause license.
|
4
4
|
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
5
5
|
|
6
|
+
require 'flo/provider/base'
|
6
7
|
require 'ostruct'
|
7
8
|
|
8
9
|
module Flo
|
9
10
|
module Provider
|
10
11
|
Response = Struct.new(:success?)
|
11
|
-
class Developer
|
12
|
+
class Developer < Flo::Provider::Base
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
14
|
+
option :password, nil, required: false
|
16
15
|
|
17
16
|
def is_successful(opts={})
|
18
17
|
success = opts[:success].nil? ? true : opts[:success]
|
@@ -23,9 +22,8 @@ module Flo
|
|
23
22
|
true
|
24
23
|
end
|
25
24
|
|
26
|
-
def
|
27
|
-
|
28
|
-
Flo::Provider::Response.new(true)
|
25
|
+
def has_option(opts={})
|
26
|
+
Flo::Provider::Response.new(options.include?(opts[:option]))
|
29
27
|
end
|
30
28
|
end
|
31
29
|
end
|
data/lib/flo/runner.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
# Licensed under the BSD 3-Clause license.
|
4
4
|
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
5
5
|
|
6
|
-
require 'flo'
|
7
6
|
require 'flo/command'
|
8
7
|
require 'flo/command_collection'
|
9
8
|
require 'flo/state'
|
@@ -48,14 +47,28 @@ module Flo
|
|
48
47
|
evaluate_file(config_file)
|
49
48
|
end
|
50
49
|
|
50
|
+
# Open and parse any available config files, in the following order:
|
51
|
+
# * ENV['FLO_CONFIG_FILE'] # Allows the addition of custom files
|
52
|
+
# * ./.flo
|
53
|
+
# * ~/.flo
|
54
|
+
# See {#load_config_file}
|
55
|
+
#
|
56
|
+
def load_default_config_files
|
57
|
+
[ENV['FLO_CONFIG_FILE'], File.join(Dir.pwd, '.flo'), File.join(Dir.home, '.flo')].compact.each do |file|
|
58
|
+
if File.exist?(file)
|
59
|
+
self.load_config_file(file)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
51
64
|
# Executes the command specified, with the arguments specified
|
52
65
|
# @param command_namespace [Array<Symbol>] An array containing the name of
|
53
66
|
# the command as a symbol, including the namespace. For example, the
|
54
67
|
# command "issue submit" would become [:issue, :submit]
|
55
|
-
# @param args=
|
68
|
+
# @param args=[] [Array] Options that will get passed to the command
|
56
69
|
#
|
57
|
-
def execute(command_namespace, args=
|
58
|
-
commands[command_namespace].call(args)
|
70
|
+
def execute(command_namespace, args=[])
|
71
|
+
commands[command_namespace][:command].call(args)
|
59
72
|
end
|
60
73
|
|
61
74
|
# @api dsl
|
@@ -76,13 +89,15 @@ module Flo
|
|
76
89
|
# DSL method: Creates and defines a {Command}, adding it to the command collection.
|
77
90
|
# Definition for the command should happen inside of the required block.
|
78
91
|
# See {Command} for methods available within the block.
|
79
|
-
# @param command_namespace [
|
80
|
-
#
|
81
|
-
# @
|
92
|
+
# @param command_namespace [String] name of the command
|
93
|
+
# @option opts [String] :summary Summary of the command
|
94
|
+
# @option opts [String] :description Detailed description of the command
|
95
|
+
# @yield [args*] The block containing the definition for the command.
|
82
96
|
# Arguments passed into {#execute} are available within the block
|
83
97
|
#
|
84
|
-
def register_command(command_namespace, &blk)
|
85
|
-
|
98
|
+
def register_command(command_namespace, opts={}, &blk)
|
99
|
+
opts[:command] = command_class.new(providers: config.providers, &blk)
|
100
|
+
commands[command_namespace] = opts
|
86
101
|
end
|
87
102
|
expose :register_command
|
88
103
|
|