rototiller 0.0.0.b → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1d55671811e2416321d690faed5b873e6656bb5a
4
- data.tar.gz: c6bd60e740eafc188f462997b429132752806a70
3
+ metadata.gz: c0293a5f48a1408895a06db0d68fc2c72f6b01f9
4
+ data.tar.gz: 618d7deee258cfabf05e3ce58444e1b8b5464978
5
5
  SHA512:
6
- metadata.gz: 2641a1a861d9f54b1218c6b718179ff985b9f2c77a2472729d2a0ba58907279e484e6c448506b83fe8c2234720a28df78e0fe2dfdfca6a823b0f7e2c3c3437b8
7
- data.tar.gz: 77a6855654474501fb1152b470e4e71d99c160065f0e46a0940e976f34fd1e9e01b139f7c336d677b8f933474978e210f751b5e41ab6b97e74232189895b83eb
6
+ metadata.gz: 55d4f136476b7486c36c2793fff8b92762dc04fe96a14654ac116e8cc2f3e96c9872f73ab102f473337d67f2a0609cb9caf88e0eb9f5a2d615b8a7b71973be37
7
+ data.tar.gz: 8780f143c25e5799bb1aa8a1cf36497d26b1c3778efb9deb7039e203646909cc2e0438b28319e70e0fe7cafcdba3a1c3202d3db1dedf7510d66df501fda24762
data/Gemfile CHANGED
@@ -2,6 +2,9 @@ source 'https://rubygems.org'
2
2
 
3
3
  # place all development, system_test, etc dependencies here
4
4
 
5
+ # in the Rakefile, so we require it in all groups
6
+ gem 'rspec' ,'~> 3.1.0'
7
+
5
8
  group :system_tests do
6
9
  #gem 'beaker', :path => "../../beaker/"
7
10
  gem 'beaker' ,'~> 2.22'
@@ -9,13 +12,13 @@ group :system_tests do
9
12
  end
10
13
 
11
14
  group :development do
12
- gem 'rspec' ,'~> 3.1.0'
13
15
  gem 'simplecov'
14
16
  #Documentation dependencies
15
17
  gem 'yard' ,'~> 0'
16
18
  gem 'markdown' ,'~> 0'
17
19
  # restrict version to enable ruby 1.9.3
18
20
  gem 'mime-types' ,'~> 2.0'
21
+ gem 'google-api-client' ,'<= 0.9.4'
19
22
  end
20
23
 
21
24
  local_gemfile = "#{__FILE__}.local"
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rototiller (0.0.0.b)
5
- rake
4
+ rototiller (0.1.0)
5
+ rake (>= 0.9.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
@@ -268,6 +268,7 @@ PLATFORMS
268
268
  DEPENDENCIES
269
269
  beaker (~> 2.22)
270
270
  beaker-hostgenerator
271
+ google-api-client (<= 0.9.4)
271
272
  markdown (~> 0)
272
273
  mime-types (~> 2.0)
273
274
  rototiller!
data/README.md CHANGED
@@ -1,57 +1,180 @@
1
- # rototiller
1
+ # Rototiller
2
2
 
3
- A shared rake task library for use at Puppet.
4
- Goals of Rototiller
3
+ A [Rake](https://github.com/ruby/rake) helper for command-oriented tasks.
5
4
 
6
- * Provide a tool that can house shared rake task code for Puppet Labs.
7
-
8
- * Reduce duplication in rakefiles across projects at Puppet Labs.
5
+ :warning: This version of Rototiller should be considered of _beta_ quality.
6
+ It is already known that the API will change quite a bit for the next release.
7
+ Please see the notes at the top of the [Write](#write) section.
9
8
 
9
+ * simplifies the building of command strings in :rototiller_task for task authors
10
+ * abstracts the overriding of command string components: commands, flags, arguments for task users
11
+ * unifies and standardizes messaging surrounding the use of environment variables for task operations
12
+ * Provides a tool that can house shared rake task code for Puppet.
13
+ * Reduce duplication in Rakefiles across projects at Puppet.
10
14
  * Reduce effort required to write first class rake tasks.
11
-
12
- * Reduce time and effort trying to understand requirement to run rake tasks.
13
-
15
+ * Reduce time and effort trying to understand requirement to run rake tasks.
14
16
  * Provide a standard interface for executing tests in a given test tier regardless of framework (Not MVP)
15
17
 
16
- ## Classes & Modules under development
17
- For more detailed documentation please refer to the yard docs.
18
-
19
- ####[EnvVar](lib/rototiller/utilities/env_var.rb)
20
-
21
- A class that tracks the state of an ENV variable.
22
- This class is responsible for formatting its own messaging, the value that should be used, and if a task should stop.
23
-
24
- ####[Flag](lib/rototiller/utilities/flag.rb)
25
-
26
- A class that tracks the desired state of command line flags used in test invocation.
27
- The desired state of these flags is determined by the user.
28
-
29
- ####[ParamCollection](lib/rototiller/utilities/param_collection.rb)
30
-
31
- A class to contain EnvVar and Flag classes.
32
- Behaves similar to an Array.
33
-
34
- ####[RototillerTask](lib/rototiller/task/rototiller_task.rb)
35
-
36
- A class used to build a rake task.
37
- Similar in approach to [RSpec::Core::RakeTask](https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/rake_task.rb)
38
-
39
- ####[CLIFlags](lib/rototiller/task/flags/cli_flags.rb)
40
-
41
- A class to contain the known CLI flags.
18
+ <a name="install"></a>
19
+ ## Install
20
+ gem install rototiller
21
+
22
+ <a name="write"></a>
23
+ ## Write
24
+ Rototiller provides a Rake DSL addition called 'rototiller_task' which is a fully featured Rake task with environment variable handling, messaging and command-string-building functionality.
25
+
26
+ :warning: The API below will change for the next release.
27
+ The known changes include (not comprehensive):
28
+ * moving `#add_flag` to `Command` and renaming it `#add_option`
29
+ * adding `#add_env` to `Command` and `#add_option`, so one can add multiple environment variables
30
+ * adding `#add_switch` to Command so one does not have to use the `:is_boolean` parameter for `#add_flag`
31
+ * adding some sort of env_var type so one does not have to use the `:required` parameter for `#add_flag`
32
+ * the above will allow for multiple commands in a task with independent option, switch, and environment variable tracking
33
+
34
+ Examples (see the [Use](#use) section for outputs):
35
+ require 'rototiller'
36
+
37
+ desc "task dependencies work. this one also uses an environment variable"
38
+ rototiller_task :parent_task do |task|
39
+ # most method initializers take either a hash, or block syntax (see next task)
40
+ task.add_env({:name => 'RANDOM_VAR', :default => 'default value'})
41
+ task.add_command({:name => "echo 'i am testing everything with $RANDOM_VAR = #{ENV['RANDOM_VAR']}'"})
42
+ end
43
+
44
+ desc "override command-name with environment variable"
45
+ rototiller_task :test => :parent_task do |task|
46
+ # block syntax here. We give up some lines for more readability
47
+ task.add_command do |cmd|
48
+ cmd.name = 'test'
49
+ cmd.override_env = 'ECHO_EXECUTABLE'
50
+ end
51
+ task.add_flag({:name => '-f', :default => 'Rakefile'})
52
+ end
53
+
54
+ desc "override flag values with environment variables"
55
+ rototiller_task :test_flag_env do |task|
56
+ task.add_command do |cmd|
57
+ cmd.name = 'test'
58
+ end
59
+ task.add_flag do |flag|
60
+ flag.name = '-f'
61
+ flag.default = 'Rakefile'
62
+ flag.override_env = 'FLAG_VALUE'
63
+ end
64
+ end
65
+
66
+ desc "do not include flag if the final value (either the default or override_env) is nil or empty and not required. add and control switches or boolean flags"
67
+ rototiller_task :test_flag_env do |task|
68
+ task.add_command do |cmd|
69
+ cmd.name = 'test'
70
+ end
71
+ task.add_flag do |flag|
72
+ flag.name = '-f'
73
+ flag.default = ''
74
+ flag.override_env = 'FLAG_VALUE'
75
+ flag.required = false
76
+ end
77
+ # examples:
78
+ # add a boolean option (switch)
79
+ #task.add_flag({:name => '--switch1', :is_boolean => true})
80
+ # add a switch which defaults to "off"
81
+ #task.add_flag({:name => '-s', :default => '', :is_boolean => true})
82
+ # add a switch with environment override
83
+ #task.add_flag({:name => '--switch3', :is_boolean => true, :override_env => 'TEST_FLAG_ENV_SWITCH3'})
84
+ end
85
+
86
+
87
+ desc "override command argument values with environment variables"
88
+ rototiller_task :test_arg_env do |task|
89
+ task.add_command do |cmd|
90
+ cmd.name = 'ls'
91
+ cmd.argument = 'Rakefile'
92
+ cmd.argument_override_env = 'FILENAME'
93
+ end
94
+ end
95
+
96
+ <a name="use"></a>
97
+ ## Use
98
+ (with the above sample Rakefile)
99
+
100
+ $) rake -T
101
+ rake parent_task # some parent task
102
+ rake test # test all the things
103
+
104
+ $) rake -D
105
+ rake parent_task
106
+ task dependencies work. this one also uses an environment variable
107
+ rake test
108
+ override command-name with environment variable
109
+
110
+ # added environment variable defaults are set, implicitly, if not found
111
+ # this way, their value can be used in the task
112
+ $) rake test
113
+ INFO: The environment variable: 'RANDOM_VAR' was found with value: 'default value':
114
+ i am testing everything with $RANDOM_VAR = default value
115
+ The CLI flag -f will be used with value Rakefile.
116
+
117
+ $) rake parent_task RANDOM_VAR=redrum
118
+ INFO: The environment variable: 'RANDOM_VAR' was found with value: 'redrum':
119
+ i am testing everything with $RANDOM_VAR = redrum
120
+
121
+ $) rake test ECHO_EXECUTABLE='ls' --verbose
122
+ INFO: The environment variable: 'RANDOM_VAR' was found with value: 'default value':
123
+ echo 'i am testing everything with $RANDOM_VAR = default value'
124
+ i am testing everything with $RANDOM_VAR = default value
125
+ The CLI flag -f will be used with value Rakefile.
126
+
127
+ ls -f Rakefile
128
+ Rakefile
129
+
130
+ $) rake test_flag_env
131
+ The CLI flag -f will be used with value Rakefile.
132
+ $) echo $?
133
+ 0
134
+
135
+ $) rake test_flag_env --verbose
136
+ The CLI flag -f will be used with value Rakefile.
137
+
138
+ test -f Rakefile
139
+
140
+ $) rake test_flag_env --verbose FLAG_VALUE='README.md'
141
+ The CLI flag -f will be used with value README.md.
142
+
143
+ test -f README.md
144
+
145
+ $) rake test_flag_env --verbose FLAG_VALUE='nonesuch'
146
+ The CLI flag -f will be used with value README.md.
147
+
148
+ test -f README.md
149
+ test -f nonesuch failed
150
+
151
+ $) rake test_arg_env
152
+ Rakefile
153
+
154
+ $) rake test_arg_env FILENAME=README.md
155
+ README.md
156
+
157
+ ## Issues
158
+
159
+ * none. it's perfect
160
+ * [Jira: Rototiller](https://tickets.puppetlabs.com/issues/?jql=project%20%3D%20QA)
42
161
 
43
162
  ## More Documentation
44
163
 
45
-
46
- Rototiller is documented using yard
47
- to view yard docs
164
+ Rototiller is documented using yard
165
+ to view yard docs, including internal Classes and Modules:
48
166
 
49
167
  First build a local copy of the gem
50
168
 
51
- $ bundle exec rake build
52
-
169
+ $) bundle exec rake build
170
+
53
171
  Next start the yard server
54
172
 
55
- $ bundle exec yard server
56
-
57
- Finally navigate to http://0.0.0.0:8808/ to view the documentation
173
+ $) bundle exec yard server
174
+
175
+ Finally navigate to http://0.0.0.0:8808/ to view the documentation
176
+
177
+ ## Maintainers
178
+ * [Zach Reichert](zach.reichert@puppetlabs.com), github:[zreichert](https://github.com/zreichert), jira:zach.reichert
179
+ * [Eric Thompson](erict@puppetlabs.com), github:[er0ck](https://github.com/er0ck), jira:erict
180
+ * [QA](qa-team@puppetlabs.com)
data/Rakefile CHANGED
@@ -1,56 +1,72 @@
1
1
  require "bundler/gem_tasks"
2
2
  require 'rspec/core/rake_task'
3
+ require 'rototiller'
3
4
 
4
5
  task :default => :test
5
6
 
6
7
  desc "Run spec tests"
7
8
  RSpec::Core::RakeTask.new(:test) do |t|
8
9
  t.rspec_opts = ['--color']
9
- t.pattern = 'spec/'
10
+ t.pattern = ENV['SPEC_PATTERN']
10
11
  end
11
12
 
13
+ task :test => [:check_test]
14
+
12
15
  task :generate_host_config do |t, args|
13
- if ENV["BEAKER_CONFIG"]
14
- next
15
- end
16
16
 
17
17
  target = ENV["TEST_TARGET"] || 'centos7-64'
18
- generate = "bundle exec beaker-hostgenerator"
18
+ generate = "beaker-hostgenerator"
19
19
  generate += " #{target}"
20
20
  generate += " > acceptance/hosts.cfg"
21
21
  sh generate
22
22
  sh "cat acceptance/hosts.cfg"
23
23
  end
24
24
 
25
- task acceptance: :generate_host_config
26
-
27
- desc 'Run acceptance tests for Rototiller'
28
- task :acceptance do |t, args|
29
-
30
- config = ENV["BEAKER_CONFIG"] || 'acceptance/hosts.cfg'
31
-
32
- preserve_hosts = ENV["BEAKER_PRESERVEHOSTS"] || 'never'
33
- type = 'pe'
34
- keyfile = ENV["BEAKER_KEYFILE"] || "#{ENV['HOME']}/.ssh/id_rsa-acceptance"
35
- load_path = ENV["BEAKER_LOADPATH"] || 'acceptance/lib'
36
- pre_suite = ENV["BEAKER_PRESUITE"] || 'acceptance/pre-suite'
37
- post_suite = ENV["BEAKER_POSTSUITE"] || ''
38
- test_suite = ENV["BEAKER_TESTSUITE"] || 'acceptance/tests'
39
- opts = ENV["BEAKER_OPTS"] || ''
40
-
41
- beaker = "bundle exec beaker "
42
- beaker += " --xml"
43
- beaker += " --debug"
44
- beaker += " --root-keys"
45
- beaker += " --repo-proxy"
46
- beaker += " --preserve-hosts #{preserve_hosts}" if preserve_hosts != ''
47
- beaker += " --config #{config}" if config != ''
48
- beaker += " --type #{type}" if type != ''
49
- beaker += " --keyfile #{keyfile}" if keyfile != ''
50
- beaker += " --load-path #{load_path}" if load_path != ''
51
- beaker += " --pre-suite #{pre_suite}" if pre_suite != ''
52
- beaker += " --post-suite #{post_suite}" if post_suite != ''
53
- beaker += " --tests #{test_suite}" if test_suite != ''
54
- beaker += " #{opts}" if opts != ''
55
- sh beaker
25
+ rototiller_task :acceptance => [:generate_host_config] do |t|
26
+ # with a hash
27
+ t.add_env({:name => 'TEST_TARGET',:default => 'centos7-64', :message => 'The argument to pass to beaker-hostgenerator'})
28
+
29
+ # with new block syntax
30
+ t.add_flag do |flag|
31
+ flag.name = '--hosts'
32
+ flag.default = 'acceptance/hosts.cfg'
33
+ flag.message = 'The configuration file that Beaker will use'
34
+ flag.override_env = 'BEAKER_HOSTS'
35
+ end
36
+ t.add_flag do |flag|
37
+ flag.name = '--preserve-hosts'
38
+ flag.default = 'onfail'
39
+ flag.message = 'The beaker setting to preserve a provisioned host'
40
+ flag.override_env = 'BEAKER_PRESERVE-HOSTS'
41
+ end
42
+ t.add_flag do |flag|
43
+ flag.name = '--keyfile'
44
+ flag.default ="#{ENV['HOME']}/.ssh/id_rsa-acceptance"
45
+ flag.message = 'The SSH key used to access a SUT'
46
+ flag.override_env = 'BEAKER_KEYFILE'
47
+ end
48
+ t.add_flag do |flag|
49
+ flag.name = '--load-path'
50
+ flag.default = 'acceptance/lib'
51
+ flag.message = 'The load path Beaker will use'
52
+ flag.override_env = "BEAKER_LOAD-PATH"
53
+ end
54
+ t.add_flag do |flag|
55
+ flag.name = '--pre-suite'
56
+ flag.default = 'acceptance/pre-suite'
57
+ flag.message = 'THe path to a directory containing pre-suites'
58
+ flag.override_env = "BEAKER_PRE-SUITE"
59
+ end
60
+ t.add_flag do |flag|
61
+ flag.name = '--tests'
62
+ flag.default = 'acceptance/tests'
63
+ flag.message = 'The path to the tests you want beaker to run'
64
+ flag.override_env = 'BEAKER_TESTS'
65
+ end
66
+
67
+ t.add_command({:name => 'beaker --debug', :override_env => 'BEAKER_EXECUTABLE'})
68
+ end
69
+
70
+ Rototiller::Task::RototillerTask.define_task :check_test do |t|
71
+ t.add_env({:name => 'SPEC_PATTERN', :default => 'spec/', :message => 'The pattern RSpec will use to find tests'})
56
72
  end
data/Rakefile.bak ADDED
@@ -0,0 +1,72 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+ require 'rototiller'
4
+
5
+ task :default => :test
6
+
7
+ desc "Run spec tests"
8
+ RSpec::Core::RakeTask.new(:test) do |t|
9
+ t.rspec_opts = ['--color']
10
+ t.pattern = ENV['SPEC_PATTERN']
11
+ end
12
+
13
+ task :test => [:check_test]
14
+
15
+ task :generate_host_config do |t, args|
16
+
17
+ target = ENV["TEST_TARGET"] || 'centos7-64'
18
+ generate = "beaker-hostgenerator"
19
+ generate += " #{target}"
20
+ generate += " > acceptance/hosts.cfg"
21
+ sh generate
22
+ sh "cat acceptance/hosts.cfg"
23
+ end
24
+
25
+ rototiller_task :acceptance => [:generate_host_config] do |t|
26
+ # with a hash
27
+ t.add_env({:name => 'TEST_TARGET',:default => 'centos7-64', :message => 'The argument to pass to beaker-hostgenerator'})
28
+
29
+ # with new block syntax
30
+ t.add_flag do |flag|
31
+ flag.name = '--hosts'
32
+ flag.default = 'acceptance/hosts.cfg'
33
+ flag.message = 'The configuration file that Beaker will use'
34
+ flag.override_env = 'BEAKER_HOSTS'
35
+ end
36
+ t.add_flag do |flag|
37
+ flag.name = '--preserve-hosts'
38
+ flag.default = 'onfail'
39
+ flag.message = 'The beaker setting to preserve a provisioned host'
40
+ flag.override_env = 'BEAKER_PRESERVE-HOSTS'
41
+ end
42
+ t.add_flag do |flag|
43
+ flag.name = '--keyfile'
44
+ flag.default ="#{ENV['HOME']}/.ssh/id_rsa-acceptance"
45
+ flag.message = 'The SSH key used to access a SUT'
46
+ flag.override_env = 'BEAKER_KEYFILE'
47
+ end
48
+ t.add_flag do |flag|
49
+ flag.name = '--load-path'
50
+ flag.default = 'acceptance/lib'
51
+ flag.message = 'The load path Beaker will use'
52
+ flag.override_env = "BEAKER_LOAD-PATH"
53
+ end
54
+ t.add_flag do |flag|
55
+ flag.name = '--pre-suite'
56
+ flag.default = 'acceptance/pre-suite'
57
+ flag.message = 'THe path to a directory containing pre-suites'
58
+ flag.override_env = "BEAKER_PRE-SUITE"
59
+ end
60
+ t.add_flag do |flag|
61
+ flag.name = '--tests'
62
+ flag.default = 'acceptance/tests'
63
+ flag.message = 'The path to the tests you want beaker to run'
64
+ flag.override_env = 'BEAKER_TESTS'
65
+ end
66
+
67
+ t.add_command({:name => 'beaker --debug', :override_env => 'BEAKER_EXECUTABLE'})
68
+ end
69
+
70
+ Rototiller::Task::RototillerTask.define_task :check_test do |t|
71
+ t.add_env({:name => 'SPEC_PATTERN', :default => 'spec/', :message => 'The pattern RSpec will use to find tests'})
72
+ end
@@ -3,12 +3,8 @@ require 'rototiller/task/rototiller_task'
3
3
  module Rake
4
4
  module DSL
5
5
 
6
- def acceptance_task(*args, &block)
7
- # Default task description
8
- # can be overridden with 'desc' method
9
- desc "Tests in the 'Acceptance' tier" unless ::Rake.application.last_comment
10
- Rototiller::Task::RototillerTask.define_task :acceptance, &block
6
+ def rototiller_task(*args, &block)
7
+ Rototiller::Task::RototillerTask.define_task(*args, &block)
11
8
  end
12
-
13
9
  end
14
10
  end
@@ -2,13 +2,17 @@ require 'rototiller/utilities/env_var'
2
2
  require 'rototiller/utilities/param_collection'
3
3
  require 'rototiller/utilities/env_collection'
4
4
  require 'rototiller/utilities/flag_collection'
5
+ require 'rototiller/utilities/command_flag'
6
+ require 'rototiller/utilities/command'
7
+ require 'rototiller/utilities/block_syntax_object'
5
8
  require 'rake/tasklib'
6
9
 
7
10
  module Rototiller
8
11
  module Task
9
12
  class RototillerTask < ::Rake::TaskLib
10
- attr_accessor :name
11
- attr_accessor :command
13
+ #TODO rename instance vars and methods to not match sub blocks
14
+ attr_reader :name
15
+ attr_reader :command
12
16
 
13
17
  # Whether or not to fail Rake when an error occurs (typically when
14
18
  # examples fail). Defaults to `true`.
@@ -20,7 +24,9 @@ module Rototiller
20
24
  def initialize(*args, &task_block)
21
25
  @name = args.shift
22
26
  @fail_on_error = true
23
- @command = 'echo empty RototillerTask. You should define a command, send a block, or EnvVar to track.'
27
+ #TODO refactor or remove
28
+ @command = Rototiller::Command.new
29
+ @command.name = 'echo empty RototillerTask. You should define a command, send a block, or EnvVar to track.'
24
30
  # rake's in-task implied method is true when using --verbose
25
31
  @verbose = verbose == true
26
32
  @env_vars = EnvCollection.new
@@ -35,29 +41,72 @@ module Rototiller
35
41
  self.new(*args, &task_block)
36
42
  end
37
43
 
38
- #TODO add arg validation to EnvVar and CommandFlag
39
- # add_env(EnvVar.new(), EnvVar.new(), EnvVar.new())
40
- # add_env('FOO', 'This is how you use FOO', 'default_value')
41
- def add_env(*args)
42
- args.all?{ |arg| arg.is_a?(EnvVar)} ? @env_vars.push(*args) : @env_vars.push(EnvVar.new(*args))
44
+ # adds environment variables to be tracked
45
+ # @param [Hash] args hashes of information about the environment variable
46
+ # @option args [String] :name The environment variable
47
+ # @option args [String] :default The default value for the environment variable
48
+ # @option args [String] :message A message describing the use of this variable
49
+ # @option args [Boolean] :required Is used internally by CommandFlag, ignored for a standalone EnvVar
50
+ #
51
+ # for block {|a| ... }
52
+ # @yield [a] Optional block syntax allows you to specify information about the environment variable, available methods track hash keys
53
+ def add_env(*args,&block)
54
+ raise ArgumentError.new("add_env takes a block or a hash") if !args.empty? && block_given?
55
+ attributes = [:name, :default, :message, :required]
56
+ add_param(@env_vars, EnvVar, attributes, args, {:set_env => true}, &block)
43
57
  end
44
58
 
45
- #TODO add arg validation to CommandFlag
46
- # add_flag(CommandFlag.new(), CommandFlag.new(), CommandFlag.new())
47
- # add_flag('--foo', 'This is how you use --foo', 'default_value')
48
- def add_flag(*args)
49
- args.all?{ |arg| arg.is_a?(CommandFlag)} ? @flags.push(*flags) : @flags.push(CommandFlag.new(*args))
59
+ # adds command line flags to be used in a command
60
+ # @param [Hash] args hashes of information about the command line flag
61
+ # @option args [String] :name The command line flag
62
+ # @option args [String] :value The value for the command line flag
63
+ # @option args [String] :message A message describing the use of this command line flag
64
+ # @option args [String] :override_env An environment variable used to override the flag value
65
+ # @option args [Boolean] :required Indicates whether an error should be raised
66
+ # if the value is nil or empty string, vs not including the flag.
67
+ # @option args [Boolean] :is_boolean Is the flag really a switch? Is it a boolean-flag?
68
+ #
69
+ # for block {|a| ... }
70
+ # @yield [a] Optional block syntax allows you to specify information about the command line flag, available methods track hash keys
71
+ def add_flag(*args, &block)
72
+ raise ArgumentError.new("add_flag takes a block or a hash") if !args.empty? && block_given?
73
+ attributes = [:name, :default, :message, :override_env, :required, :is_boolean]
74
+ add_param(@flags, CommandFlag, attributes, args, &block)
75
+ end
76
+
77
+ # adds command to be executed by task
78
+ # @param [Hash] args hash of information about the command to be executed
79
+ # @option arg [String] :name The command to be executed
80
+ # @option arg [String] :override_env An environment variable used to override the command to be executed by the task
81
+ #
82
+ # for block {|a| ... }
83
+ # @yield [a] Optional block syntax allows you to specify information about command, available methods track hash keys
84
+ def add_command(args={}, &block)
85
+ attributes = [:name, :override_env, :argument, :argument_override_env]
86
+ if block_given?
87
+ attribute_hash = pull_params_from_block(attributes, &block).to_h
88
+ else
89
+ attribute_hash = args
90
+ end
91
+ @command = Rototiller::Command.new(attribute_hash)
50
92
  end
51
93
 
52
94
  private
53
95
 
54
96
  # @private
55
- def run_task
97
+ def print_messages
56
98
  puts @flags.format_messages
57
99
  puts @env_vars.format_messages
58
- exit if @env_vars.stop?
100
+ exit_code = 1
101
+ exit exit_code if @env_vars.stop? || @flags.stop?
102
+ end
59
103
 
60
- command_str = @command << @flags.to_s
104
+ # @private
105
+ def run_task
106
+ print_messages
107
+ command_str = [
108
+ (@command.name if @command.name), @flags.to_s, (@command.argument if @command.argument)
109
+ ].delete_if{ |i| [nil, '', false].any?{|forbidden| i == forbidden}}.join(' ')
61
110
  puts command_str if @verbose
62
111
 
63
112
  return if system(command_str)
@@ -75,7 +124,7 @@ module Rototiller
75
124
  def define(args, &task_block)
76
125
  # Default task description
77
126
  # can be overridden with standard 'desc' DSL method
78
- desc "RototillerTask: A Task with optional environment variable and command flag tracking" unless ::Rake.application.last_comment
127
+ desc 'RototillerTask: A Task with optional environment-variable and command-flag tracking' unless ::Rake.application.last_description
79
128
 
80
129
  task(@name, *args) do |_, task_args|
81
130
  RakeFileUtils.__send__(:verbose, @verbose) do
@@ -90,6 +139,34 @@ module Rototiller
90
139
  def set_verbose(verbosity=true)
91
140
  @verbose = verbosity
92
141
  end
142
+
143
+ # @private
144
+ def add_param(collection, param_class, param_array, args, opts={}, &block)
145
+
146
+ if block_given?
147
+
148
+ param_hash = pull_params_from_block(param_array, &block).to_h
149
+ param_hash[:set_env] = true if opts[:set_env]
150
+ collection.push(param_class.new(param_hash))
151
+ else
152
+
153
+ args.each do |arg|
154
+
155
+ #FIXME: add a test for this
156
+ raise ArgumentError.new("Argument must be a Hash. Received: '#{arg.class}'") unless arg.is_a?(Hash)
157
+ arg[:set_env] = true if opts[:set_env]
158
+ collection.push(param_class.new(arg))
159
+ end
160
+ end
161
+ end
162
+
163
+ # @private
164
+ def pull_params_from_block(param_array, &block)
165
+
166
+ block_syntax_obj = Rototiller::Block_syntax.new(param_array)
167
+ yield(block_syntax_obj)
168
+ block_syntax_obj
169
+ end
93
170
  end
94
171
  end
95
172
  end
@@ -0,0 +1,16 @@
1
+ module Rototiller
2
+ class Block_syntax
3
+
4
+ def initialize(param_array)
5
+ self.class.class_eval { param_array.each { |i| attr_accessor i } }
6
+ end
7
+
8
+ def to_h
9
+ h = Hash.new
10
+ self.instance_variables.each do |var|
11
+ h[var.to_s.delete('@').to_sym] = self.instance_variable_get(var)
12
+ end
13
+ h
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,43 @@
1
+ require 'rototiller/utilities/env_var'
2
+ module Rototiller
3
+ class Command
4
+
5
+ include ColorText
6
+
7
+ # @return [String] the command to be used, could be considered a default
8
+ attr_accessor :name
9
+
10
+ # @return [EnvVar] the ENV that is equal to this command
11
+ attr_reader :override_env
12
+
13
+ # @return [String, nil] the value that should be used as an argument to the given command
14
+ attr_reader :argument
15
+
16
+ # @return [EnvVar] the ENV that is equal to the argument to be used with this command
17
+ attr_reader :argument_override_env
18
+
19
+ # Creates a new instance of CommandFlag, holds information about desired state of a command
20
+ # @param [Hash] attribute_hash hashes of information about the command
21
+ # @option attribute_hash [String] :command The command
22
+ # @option attribute_hash [String] :override_env The environment variable that can override this command
23
+ def initialize(attribute_hash = {})
24
+
25
+ # check if an override_env is provided
26
+ if attribute_hash[:override_env]
27
+ @override_env = EnvVar.new({:name => attribute_hash[:override_env], :default => attribute_hash[:name]})
28
+ @name = @override_env.value
29
+ else
30
+ @name = attribute_hash[:name]
31
+ end
32
+
33
+ # check if an argument_override_env is provided
34
+ if attribute_hash[:argument_override_env]
35
+ @argument_override_env = EnvVar.new({:name => attribute_hash[:argument_override_env], :default => attribute_hash[:argument]})
36
+ @argument = @argument_override_env.value
37
+ else
38
+ @argument = attribute_hash[:argument]
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -1,4 +1,5 @@
1
1
  require 'rototiller/utilities/color_text'
2
+ require 'rototiller/utilities/env_var'
2
3
 
3
4
  class CommandFlag
4
5
 
@@ -10,26 +11,113 @@ class CommandFlag
10
11
  # @return [true, false, nil, String] the value if any of the flag
11
12
  attr_reader :value
12
13
 
14
+ # @return [EnvVar] the ENV that is equal to this flag
15
+ attr_reader :override_env
16
+
17
+ # @return [Boolean] whether the flag is required or not
18
+ attr_reader :required
19
+
20
+ # @return [true, nil] if the state of the EnvVar requires the task to stop
21
+ attr_reader :stop
22
+
23
+ # @return [true, nil] if this flag/option is really a switch (boolean flag)
24
+ attr_reader :is_boolean
25
+
13
26
  # Creates a new instance of CommandFlag, holds information about desired state of a CLI flag
14
- # @param flag [String] the flag to be set on a CLI '-v' or '--verbose'
15
- # @param message [String] the message describing the Flag
16
- # @param value [String] the value to use as the value if one is required
17
- def initialize(flag, value=nil, message)
18
- @flag = flag
19
- @message = message
20
- @value = value
27
+ # @param [Hash] attribute_hash hashes of information about the command line flag
28
+ # @option attribute_hash [String] :name The command line flag
29
+ # @option attribute_hash [String] :value The value for the command line flag
30
+ # @option attribute_hash [String] :message A message describing the use of this command line flag
31
+ # @option attribute_hash [String] :override_env The environment variable that can override this flags value
32
+ # @option attribute_hash [Boolean] :is_boolean Is the flag really a switch? Is it a boolean-flag?
33
+ # @option attribute_hash [Boolean] :required Indicates whether an error should be raised
34
+ # if the final value is nil or empty string, vs not including the flag.
35
+ def initialize(attribute_hash)
36
+ validate_attribute_hash(attribute_hash)
37
+
38
+ @original_name = attribute_hash[:name]
39
+ @message = attribute_hash[:message]
40
+ @is_boolean = attribute_hash[:is_boolean] || false
41
+
42
+ # handle :required
43
+ attribute_hash[:required].is_a?(String) ? attribute_hash[:required] = (attribute_hash[:required].downcase == 'true') : attribute_hash[:required]
44
+ @required = ( !!attribute_hash[:required] == attribute_hash[:required] ? attribute_hash[:required] : true)
45
+
46
+ # handle the flag/switch name
47
+ if attribute_hash[:name] && !attribute_hash[:is_boolean]
48
+ @flag = attribute_hash[:name]
49
+ else
50
+ # default takes precedence in case the default state is "off" aka: empty
51
+ default_value = attribute_hash[:default] || attribute_hash[:name]
52
+
53
+ # FIXME: whoa complexity. see fixme below
54
+ # this looks a lot like below. we need a method to handle override_env and the logic here for switches vs. options
55
+ # but we're just gonna rip all this out when CommandSwitch inherits from future CommandOption
56
+ if attribute_hash[:override_env]
57
+ # Create a new EnvVar instance and ask it what the value is
58
+ @override_env = EnvVar.new({:name => attribute_hash[:override_env], :default => default_value})
59
+ @flag = @override_env.value
60
+ else
61
+ @flag = default_value
62
+ end
63
+ end
64
+
65
+ # FIXME: this is getting complex. we should not be doing all these complex if/then in here
66
+ # we should inherit from CommandOption to form CommandSwitch which overrides is_boolean
67
+ # make is_boolean protected (private within a module, parent/child, or similar)
68
+ if @is_boolean
69
+ @value = ''
70
+ else
71
+ if attribute_hash[:default] && !attribute_hash[:override_env]
72
+ # the default is the implied hard coded value
73
+ @value = attribute_hash[:default]
74
+ else
75
+ # Create a new EnvVar instance and ask it what the value is
76
+ @override_env = EnvVar.new({:name => attribute_hash[:override_env], :default => attribute_hash[:default], :required => @required})
77
+
78
+ @value = @override_env.value
79
+ @stop = @override_env.stop
80
+ end
81
+ end
21
82
  end
22
83
 
23
84
  # The formatted message to be displayed to the user
24
85
  # @return [String] the CommandFlag's message, formatted with color
25
86
  def message
26
- green_text(@message) << "\n" << describe_flag_state
87
+ @message ? green_text(@message) << "\n" << describe_flag_state : describe_flag_state
27
88
  end
28
89
 
29
90
  private
30
91
  def describe_flag_state
31
- only_flag = "The CLI flag #{@flag} will be used, no value was provided."
32
- flag_with_value = "The CLI flag #{@flag} will be used with value #{@value}."
33
- green_text(@value.nil? ? only_flag : flag_with_value)
92
+ if @stop
93
+ required_env = "The CLI flag '#{@flag}' needs a value.\nYou can specify this value with the environment variable '#{override_env.var}'"
94
+ return red_text(required_env)
95
+ elsif !@required && (@value.nil? || @value.empty?)
96
+ flag_without_value = "The CLI flag #{@flag} has no value assigned and will not be included."
97
+ return yellow_text(flag_without_value)
98
+ else
99
+ if @is_boolean
100
+ has_flag_name = (@flag != '') && (@flag != nil)
101
+ if has_flag_name
102
+ flag_with_value = "The CLI switch '#{@flag}' will be used."
103
+ else
104
+ flag_with_value = "The CLI switch '#{@original_name}' will NOT be used."
105
+ return yellow_text(flag_with_value)
106
+ end
107
+ else
108
+ flag_with_value = "The CLI flag '#{@flag}' will be used with value '#{@value}'."
109
+ end
110
+ return green_text(flag_with_value)
111
+ end
112
+ end
113
+
114
+ def validate_attribute_hash(h)
115
+ # validate the contents of the hash
116
+ error = String.new
117
+ error << "A 'name' is required\n" unless h[:name]
118
+ error << "Cannot use 'required' with 'is_boolean'\n" unless !(h[:required] && h[:is_boolean])
119
+ error << "Must specify a 'default' or an 'override_env' unless 'is_boolean' is true\n" unless h[:default] || h[:override_env] || h[:is_boolean]
120
+
121
+ raise(ArgumentError, error) unless error.empty?
34
122
  end
35
123
  end
@@ -7,9 +7,4 @@ class EnvCollection < ParamCollection
7
7
  super
8
8
  end
9
9
 
10
- # Do any of the contents of this ParamCollection require the task to stop
11
- # @return [true, nil] should the values of this ParamCollection stop the task
12
- def stop?
13
- @collection.any?{ |param| param.stop }
14
- end
15
10
  end
@@ -1,10 +1,20 @@
1
1
  require 'rototiller/utilities/color_text'
2
2
 
3
3
  class EnvVar
4
-
4
+ MESSAGE_TYPES = {:nodefault_noexist=>0, :exist=>1, :default_noexist=>2, :not_required=>3}
5
5
  include ColorText
6
6
 
7
- attr_accessor :var, :message, :default
7
+ # @return [String] the value of the :name argument
8
+ attr_accessor :var
9
+
10
+ # @return [String] the value of the :message argument
11
+ attr_accessor :message
12
+
13
+ # @return [String] the value of the :default argument
14
+ attr_accessor :default
15
+
16
+ # @return [true, nil] If the env_var should error if no value is set. Used internally by CommandFlag, ignored for standalone EnvVar.
17
+ attr_reader :required
8
18
 
9
19
  # @return [Symbol] the debug level of the message, ':warning', ':error', ':info'
10
20
  attr_reader :message_level
@@ -12,51 +22,109 @@ class EnvVar
12
22
  # @return [true, nil] if the state of the EnvVar requires the task to stop
13
23
  attr_reader :stop
14
24
 
25
+ # @return [String] the value of the ENV based on specified default and environment state
26
+ attr_reader :value
27
+
15
28
  # Creates a new instance of EnvVar, holds information about the ENV in the environment
16
- # @param var [String] the ENV in the environment, 'HOME'
17
- # @param message [String] the message describing the ENV
18
- # @param default [String] the value to use as the default if the ENV is not present
19
- def initialize(var, default=false, message)
20
- @var = var
21
- @message = message
22
- @default = default
23
- set_message_level
24
- end
29
+ # @param [Hash] attribute_hash hash of information about the environment variable
30
+ # @option attribute_hash [String] :name The environment variable
31
+ # @option attribute_hash [String] :default The default value for the environment variable
32
+ # @option attribute_hash [String] :message A message describing the use of this variable
33
+ # @option attribute_hash [Boolean] :required Used internally by CommandFlag, ignored for a standalone EnvVar
34
+ def initialize(attribute_hash)
35
+ raise(ArgumentError, 'A name must be supplied to add_env') unless attribute_hash[:name]
36
+ @var = attribute_hash[:name]
37
+ @message = attribute_hash[:message]
38
+ @default = attribute_hash[:default]
39
+ @set_env = attribute_hash[:set_env] || false
40
+ attribute_hash[:required].is_a?(String) ? attribute_hash[:required] = (attribute_hash[:required].downcase == 'true') : attribute_hash[:required]
41
+ @required = ( !!attribute_hash[:required] == attribute_hash[:required] ? attribute_hash[:required] : true)
25
42
 
26
- # The value of the ENV determined by the EnvVar class
27
- # @return [String] the value determined by the EnvVar class
28
- def value
29
- ENV[@var] || @default
43
+ reset
30
44
  end
31
45
 
32
46
  # The formatted message to be displayed to the user
33
47
  # @return [String] the EnvVar's message, formatted for color and meaningful to the state of the EnvVAr
34
48
  def message
35
49
  if message_level == :error
36
- red_text("The ENV #{@var} is required, #{@message}")
50
+ level_str = 'ERROR:'
37
51
  elsif message_level == :info
38
- green_text("The ENV #{@var} was found in the environment with the value #{value}")
52
+ level_str = 'INFO:'
39
53
  elsif message_level == :warning
40
- yellow_text("WARNING: the ENV #{@var} is not set, proceeding with default value: #{@default}")
54
+ level_str = 'WARNING:'
55
+ end
56
+ message_prepend = "#{level_str} The environment variable: '#{@var}'"
57
+ if get_message_type == MESSAGE_TYPES[:default_noexist]
58
+ return yellow_text("#{message_prepend} is not set. Proceeding with default value: '#{@default}': #{@message}")
59
+ end
60
+ if get_message_type == MESSAGE_TYPES[:not_required]
61
+ return yellow_text("#{message_prepend} is not set, but is not required. Proceeding with no flag: #{@message}")
62
+ end
63
+ if get_message_type == MESSAGE_TYPES[:exist]
64
+ return green_text("#{message_prepend} was found with value: '#{ENV[@var]}': #{@message}")
41
65
  end
66
+ if get_message_type == MESSAGE_TYPES[:nodefault_noexist]
67
+ return red_text("#{message_prepend} is required: #{@message}")
68
+ end
69
+ end
70
+
71
+ # If any of these variables are assigned a new value after this object's creation, reset @value and @message_level.
72
+ def var=(var)
73
+ @var = var
74
+ reset
75
+ end
76
+
77
+ def default=(default)
78
+ @default = default
79
+ reset
80
+ end
81
+
82
+ def message=(message)
83
+ @message = message
84
+ reset
85
+ end
86
+
87
+ def required=(required)
88
+ @required = required
89
+ reset
42
90
  end
43
91
 
44
92
  private
93
+ def reset
94
+ # TODO should an env automatically set the ENV?
95
+ @value = ENV[@var] || @default
96
+ ENV[@var] = @value if @set_env
97
+ set_message_level
98
+ end
99
+
45
100
  def check
46
101
  ENV.key?(@var)
47
102
  end
48
103
 
49
- def set_message_level
50
- if !@default && !check
104
+ def get_message_type
105
+ if (value.nil? || value.empty?) && !required
106
+ MESSAGE_TYPES[:not_required]
107
+ elsif !@default && !check
51
108
  # ENV is not Present and it has no default value
52
- @message_level = :error
53
- @stop = true
54
- elsif !@default && check || @default && check
55
- # ENV is present and it has no default value
56
- @message_level = :info
109
+ MESSAGE_TYPES[:nodefault_noexist]
110
+ elsif check
111
+ # ENV is present; may or may not have default, who cares
112
+ MESSAGE_TYPES[:exist]
57
113
  elsif @default && !check
58
114
  # ENV is not present and it has default value
59
- @message_level = :warning
115
+ MESSAGE_TYPES[:default_noexist]
116
+ end
117
+ end
118
+
119
+ def set_message_level
120
+ case get_message_type
121
+ when MESSAGE_TYPES[:nodefault_noexist]
122
+ @message_level = :error
123
+ @stop = true
124
+ when MESSAGE_TYPES[:exist], MESSAGE_TYPES[:default_noexist], MESSAGE_TYPES[:not_required]
125
+ @message_level = :info
126
+ else
127
+ raise 'EnvVar: message type not supported'
60
128
  end
61
129
  end
62
130
  end
@@ -14,13 +14,15 @@ class FlagCollection < ParamCollection
14
14
  flag_str = String.new
15
15
 
16
16
  @collection.each do |flag|
17
- if flag.value.nil?
18
- flag_str << ' ' << flag.flag
17
+ if (flag.value.nil? || flag.value.empty?) && !flag.required
18
+ #do nothing
19
+ elsif flag.value.nil?
20
+ flag_str << flag.flag << ' '
19
21
  else
20
- flag_str << ' ' << flag.flag << ' ' << flag.value
22
+ flag_str << flag.flag << ' ' << flag.value << ' '
21
23
  end
22
24
  end
23
25
 
24
- flag_str
26
+ flag_str.rstrip
25
27
  end
26
28
  end
@@ -63,5 +63,11 @@ class ParamCollection
63
63
  end
64
64
  end
65
65
 
66
+ # Do any of the contents of this ParamCollection require the task to stop
67
+ # @return [true, nil] should the values of this ParamCollection stop the task
68
+ def stop?
69
+ @collection.any?{ |param| param.stop }
70
+ end
71
+
66
72
  private :filter_contents, :check_classes
67
73
  end
@@ -1,5 +1,5 @@
1
1
  module Rototiller
2
2
  module Version
3
- STRING = '0.0.0.b'
3
+ STRING = '0.1.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rototiller
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0.b
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
- - Puppetlabs
7
+ - Puppet Labs
8
+ - Zach Reichert
9
+ - Eric Thompson
8
10
  autorequire:
9
11
  bindir: bin
10
12
  cert_chain: []
11
- date: 2016-03-28 00:00:00.000000000 Z
13
+ date: 2016-05-16 00:00:00.000000000 Z
12
14
  dependencies:
13
15
  - !ruby/object:Gem::Dependency
14
16
  name: rake
@@ -16,15 +18,15 @@ dependencies:
16
18
  requirements:
17
19
  - - '>='
18
20
  - !ruby/object:Gem::Version
19
- version: '0'
21
+ version: 0.9.0
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
23
25
  requirements:
24
26
  - - '>='
25
27
  - !ruby/object:Gem::Version
26
- version: '0'
27
- description: Puppetlabs tool for building rake tasks
28
+ version: 0.9.0
29
+ description: Puppet Labs tool for building rake tasks
28
30
  email:
29
31
  - qa@puppetlabs.com
30
32
  executables: []
@@ -36,11 +38,14 @@ files:
36
38
  - LICENSE.md
37
39
  - README.md
38
40
  - Rakefile
41
+ - Rakefile.bak
39
42
  - lib/rototiller.rb
40
43
  - lib/rototiller/rake/dsl/dsl_extention.rb
41
44
  - lib/rototiller/task/flags/cli_flags.rb
42
45
  - lib/rototiller/task/rototiller_task.rb
46
+ - lib/rototiller/utilities/block_syntax_object.rb
43
47
  - lib/rototiller/utilities/color_text.rb
48
+ - lib/rototiller/utilities/command.rb
44
49
  - lib/rototiller/utilities/command_flag.rb
45
50
  - lib/rototiller/utilities/env_collection.rb
46
51
  - lib/rototiller/utilities/env_var.rb
@@ -63,14 +68,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
63
68
  version: '0'
64
69
  required_rubygems_version: !ruby/object:Gem::Requirement
65
70
  requirements:
66
- - - '>'
71
+ - - '>='
67
72
  - !ruby/object:Gem::Version
68
- version: 1.3.1
73
+ version: '0'
69
74
  requirements: []
70
75
  rubyforge_project:
71
76
  rubygems_version: 2.6.2
72
77
  signing_key:
73
78
  specification_version: 4
74
- summary: Puppetlabs rake tool
79
+ summary: Puppet Labs rake tool
75
80
  test_files: []
76
81
  has_rdoc: