volley 0.1.0.alpha1

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.
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/*
16
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in volley.gemspec
4
+ gemspec
5
+
6
+ gem 'rake'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Shawn Catanzarite
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Volley
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'volley'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install volley
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/volley ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'volley'
5
+ require 'clamp'
6
+
7
+ Volley::VolleyFile.init
8
+ STDOUT.sync = true
9
+
10
+ module Volley
11
+ class Command < Clamp::Command
12
+ option %w{-c --config}, "CONFIG", "configuration file", :default => "~/.Volleyfile"
13
+ option %w{-p --primary}, "PRIMARY", "primary configuration file", :default => "./Volleyfile"
14
+ option %w{-d --debug}, :flag, "set debug flag"
15
+ option %w{-f --fork}, :flag, "fork process into background"
16
+
17
+ parameter "DESCRIPTOR", "volley descriptor, follows the format: project:plan[@branch[:version]]"
18
+ parameter "[ARG] ...", "additional arguments passed to plan of the form: key:value"
19
+
20
+ def execute
21
+ Volley::VolleyFile.load(config, :optional => true)
22
+ Volley::VolleyFile.load(primary, :primary => true) if File.file?(primary)
23
+ Volley.config.debug = debug?
24
+
25
+ (project, plan, branch, version) = descriptor.split(/[:\@]/)
26
+ project = "volley" if project.nil? || project.blank?
27
+
28
+ args = arg_list
29
+ args << "branch:#{branch}" if branch
30
+ args << "version:#{version}" if version
31
+
32
+ if debug?
33
+ puts "project: #{project}"
34
+ puts "plan: #{plan}"
35
+ puts "branch: #{branch}"
36
+ puts "version: #{version}"
37
+ puts "args_list: #{args.join(",")}"
38
+ end
39
+
40
+ #raise "you must specify project (#{project}): [#{Volley::Dsl.projects.keys.join(',')}]" unless project && Volley::Dsl.project(project)
41
+ #raise "you must specify plan: #{project} [#{Volley::Dsl::Project.project(project).plans.keys.join(", ")}]" unless plan && Volley::Dsl::Project.project(project).plan(plan)
42
+
43
+ Volley.process(:project => project, :plan => plan, :branch => branch, :version => version, :args => args)
44
+ rescue Interrupt => e
45
+ Volley::Log.warn "Cancelled..."
46
+ rescue => e
47
+ Volley::Log.error "error: #{e.message}"
48
+ Volley::Log.error e if debug?
49
+ Volley::Log.debug e
50
+ raise Clamp::HelpWanted, self
51
+ end
52
+ end
53
+ end
54
+
55
+ Volley::Command.run
@@ -0,0 +1,106 @@
1
+ project :volley do
2
+ plan :init do
3
+ default do
4
+ file = File.expand_path("../init/Volleyfile", __FILE__)
5
+ dest = "#{Dir.pwd}/Volleyfile"
6
+ FileUtils.copy(file, dest)
7
+ puts "created: #{dest}"
8
+ end
9
+ end
10
+ plan :list do
11
+ default do
12
+ Volley::Dsl::Project.projects.each do |p, project|
13
+ Volley::Log.info "project: #{p}"
14
+ project.plans.each do |pl, plan|
15
+ Volley::Log.info ".. #{pl} #{plan.usage}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ plan :latest do
21
+ argument :project #, :required => true
22
+ argument :branch #, :required => true
23
+ default do
24
+ project = args.project
25
+ branch = args.branch
26
+
27
+ if project.nil? && rawargs
28
+ first = rawargs.first
29
+ (p, b) = first.split(/\//) if first
30
+ if p
31
+ project = p
32
+ branch = b
33
+ end
34
+ end
35
+ raise "project and branch must be specified" unless project && branch
36
+
37
+ pub = Volley::Dsl.publisher
38
+ puts pub.latest(project, branch)
39
+ end
40
+ end
41
+ plan :versions do
42
+ argument :project
43
+ argument :branch
44
+ argument :version
45
+ argument :all, :convert => :boolean, :default => false
46
+ argument :output, :default => "list", :convert => :to_sym, :choices => ["json", "xml", "list"]
47
+
48
+ default do
49
+ project = args.project
50
+ branch = args.branch
51
+ version = args.version
52
+
53
+ if project.nil? && rawargs
54
+ first = rawargs.first
55
+ (p, b, v) = first.split(/\//) if first
56
+ if p
57
+ project = p
58
+ branch = b
59
+ version = v
60
+ end
61
+ end
62
+
63
+ pub = Volley::Dsl.publisher
64
+ data = []
65
+ if args.all
66
+ data = pub.all.keys.reject {|e| e =~ /latest$/}
67
+ data.each { |k, v| puts "%2s %s" % [v, k] }
68
+ else
69
+ if project
70
+ if branch
71
+ if version
72
+ data = pub.contents(project, branch, version)
73
+ else
74
+ data = pub.versions(project, branch)
75
+ end
76
+ else
77
+ data = pub.branches(project)
78
+ end
79
+ else
80
+ data = pub.projects
81
+ end
82
+
83
+ case args.output
84
+ when :json
85
+ puts data.to_json
86
+ when :xml
87
+ puts data.to_xml
88
+ else
89
+ data.each { |e| puts e }
90
+ end
91
+ end
92
+ end
93
+ end
94
+ plan :remote do
95
+ argument :version, :required => true
96
+
97
+ default do
98
+ pub = Volley::Dsl.publisher
99
+ (pr, br, vr) = args.version.split(/[\:\/\.]/)
100
+ vr ||= 'latest'
101
+ vf = pub.volleyfile(:project => pr, :branch => br, :version => vr)
102
+ load vf
103
+ volley :project => "volley", :plan => "list"
104
+ end
105
+ end
106
+ end
data/init/Volleyfile ADDED
@@ -0,0 +1,104 @@
1
+ # TODO: implement source control framework
2
+ # Source Control - access to source control information from within actions
3
+ # scm :svn
4
+ # or
5
+ # scm :git
6
+ #
7
+ # Configuring one of these, enables you to use source control
8
+ # information inside actions:
9
+ # action :name do
10
+ # args.branch = source.branch
11
+ # args.version = source.revision
12
+ # end
13
+
14
+ project :myproject do
15
+ plan :push do
16
+ # Encryption allows you to share artifact files on public data stores.
17
+ #
18
+ # encryption is turned off by default
19
+ # encrypt false
20
+ # to enable encryption you must supply a keyfile
21
+ # encrypt true, :key => "/path/to/keyfile"
22
+ # or supply a key directly
23
+ # encrypt true, :key => "this is the key"
24
+
25
+ # Command output is disabled by default
26
+ # output false
27
+ # setting this to true, will display output
28
+ # from all commands that are run as part
29
+ # of the plan
30
+ # output true
31
+
32
+ # Arguments are captured from the command line
33
+ # in key:value pairs.
34
+ #
35
+ # By specifying the following:
36
+ # argument :branch
37
+ # You will then have access to args.branch within your plan's actions
38
+ #
39
+ # Argument options
40
+ # :default set the default value for the argument
41
+ # :convert convert the value received from the command line (see below)
42
+ # :required raise an error if this argument is not specified
43
+ #
44
+ # Convert Option:
45
+ # :convert => :boolean
46
+ # :convert => :to_s
47
+ # :convert => :to_i
48
+ # Boolean is a custom conversion, all others are delegated to the string class
49
+ #
50
+
51
+ # Actions
52
+ # Actions are executed in order that they are defined in.
53
+ #
54
+ # Generally you will use action wrappers (like: build and push)
55
+ #
56
+ # The build action is a specialized action that *requires*
57
+ # the return value of the block to be the list of files to add
58
+ # to the artifact.
59
+ #
60
+ # build do |attr|
61
+ # # run build commands
62
+ # ["target/something.war"]
63
+ # end
64
+ #
65
+ # By default, the build action will also configure the 'pack' action
66
+ # (which handles creating the final combined artifact) and the 'encrypt'
67
+ # action, if encryption is enabled.
68
+ #
69
+ # The 'push' action is a shortcut to Publish the artifact to a data store.
70
+ # push :publisher
71
+ # Volley includes the Amazon S3 publisher.
72
+ # push :amazons3
73
+ #
74
+ # You also have the ability to add actions directly:
75
+ #
76
+ # action :name do
77
+ # # do something
78
+ # end
79
+
80
+ build do |attr|
81
+ clean = args.clean ? "clean" : ""
82
+
83
+ # to run commands inside of actions, use the shellout method
84
+ # this is a wrapper around Mixlib::Shellout
85
+ shellout("rake #{clean} build")
86
+
87
+ # the 'branch' argument is generally set to the branch name.
88
+ # by using the ||= operator here, we can also set the 'branch' argument on the command line.
89
+ args.branch ||= source.branch
90
+ # the 'version' argument is generally set to the source control revision number.
91
+ # by using the ||= operator here, we can also set the 'branch' argument on the command line.
92
+ args.version ||= source.revision
93
+
94
+ final = "target/awesome.war"
95
+ [final]
96
+ end
97
+
98
+ push :amazons3
99
+ end
100
+
101
+ plan :deploy do
102
+ pull :amazons3
103
+ end
104
+ end
@@ -0,0 +1,8 @@
1
+
2
+ module Volley
3
+ class << self
4
+ def config
5
+ @config ||= OpenStruct.new({})
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,339 @@
1
+ require 'tempfile'
2
+
3
+ module Volley
4
+ module Dsl
5
+ class Plan
6
+ attr_accessor :rawargs
7
+
8
+ def initialize(name, o={ }, &block)
9
+ options = {
10
+ :name => name,
11
+ :output => false,
12
+ :project => nil,
13
+ :encrypt => false,
14
+ :pack => true,
15
+ :pack_type => "tgz",
16
+ }.merge(o)
17
+ raise "project instance must be set" if options[:project].nil?
18
+ @project = options[:project]
19
+ @block = block
20
+ @attributes = OpenStruct.new(options)
21
+ @args = OpenStruct.new
22
+ @argdefs = {}
23
+ @argdata = {}
24
+ @actions = {:pre => [], :main => [], :post => []}
25
+ instance_eval &block if block_given?
26
+ end
27
+
28
+ def call(options={})
29
+ process_arguments(options[:rawargs])
30
+ #instance_eval &@block
31
+ run_actions
32
+ end
33
+
34
+ def full_usage
35
+ out = []
36
+ @argdefs.each do |n, arg|
37
+ t = arg[:convert] || "string"
38
+ r = arg[:required]
39
+ #o = "#{n}:#{t}"
40
+ #o = "[#{o}]" unless r
41
+ d = arg[:default] ? "default: #{arg[:default]}" : ""
42
+ o = "%15s %15s %1s %s" % [n, t, (r ? '*' : ''), d]
43
+ out << "#{o}"
44
+ end
45
+ out
46
+ end
47
+
48
+ def usage
49
+ out = []
50
+ @argdefs.each do |n, arg|
51
+ t = arg[:convert] || "string"
52
+ r = arg[:required]
53
+ d = arg[:default] ? "#{arg[:default]}" : ""
54
+ v = arg[:choices] ? "[#{arg[:choices].join(",")}]" : "<#{n}>"
55
+ out << "#{n}:#{v}#{"*" if r}"
56
+ end
57
+ out.join(" ")
58
+ end
59
+
60
+ def run_actions(*stages)
61
+ stages = [*stages].flatten
62
+ stages = [:pre, :main, :post] if stages.count == 0
63
+ #ap Volley.config if Volley.config.debug
64
+ stages.each do |stage|
65
+ Volley::Log.debug "running actions[:#{stage}]:" if @actions[stage].count > 0
66
+ @actions[stage].each do |act|
67
+ Volley::Log.debug "running action: #{act[:name]}"
68
+ #ap act if Volley.config.debug
69
+ self.instance_eval(&act[:block])
70
+ end
71
+ end
72
+ #ap self if Volley.config.debug
73
+ end
74
+
75
+ def method_missing(n, *args)
76
+ Volley::Log.warn "** plan DSL does not support method: #{n} #{args.join(',')}"
77
+ raise "not supported"
78
+ end
79
+
80
+ def args
81
+ @args
82
+ end
83
+
84
+ def source
85
+ @project.source or raise "SCM not configured"
86
+ end
87
+
88
+ def load(file)
89
+ real = File.expand_path(file)
90
+ Volley::VolleyFile.load(real)
91
+ rescue => e
92
+ Volley::Log.error "failed to load file: #{e.message}: #{file} (#{real})"
93
+ Volley::Log.debug e
94
+ end
95
+
96
+ def argument(name, opts={ })
97
+ @argdefs[name] = opts
98
+ action "argument-#{name}", :pre do
99
+ n = name.to_sym
100
+ # had to make this more complex to handle valid "false" values
101
+ v = begin
102
+ if @argdata[n].nil?
103
+ if opts[:default].nil?
104
+ nil
105
+ else
106
+ opts[:default]
107
+ end
108
+ else
109
+ @argdata[n]
110
+ end
111
+ end
112
+ raise "arg '#{name}' is required, but not set" if opts[:required] && v.nil?
113
+ if opts[:convert]
114
+ if opts[:convert] == :boolean
115
+ v = boolean(v)
116
+ else
117
+ v = v.send(opts[:convert])
118
+ end
119
+ elsif block_given?
120
+ v = yield v
121
+ end
122
+ raise "arg '#{name}' is required, but not set (after convert)" if opts[:required] && v.nil?
123
+ @args.send("#{n}=", v)
124
+ @attributes.send("#{n}=", v) if opts[:attr]
125
+ end
126
+ end
127
+
128
+ def output(tf=true)
129
+ @attributes.output = tf
130
+ argument :output, :attr => true, :default => tf, :convert => :boolean
131
+ end
132
+
133
+ def default(&block)
134
+ action(:default, :main, &block)
135
+ end
136
+
137
+ def action(name, stage=:main, &block)
138
+ n = name.to_sym
139
+ @actions[stage] << { :name => n, :stage => stage, :block => block }
140
+ end
141
+
142
+ def push(&block)
143
+ action :files, :post do
144
+ list = begin
145
+ case block.arity
146
+ when 1
147
+ yield @attributes.dup
148
+ else
149
+ yield
150
+ end
151
+ end
152
+ list = [*list].flatten
153
+ notfound = list.reject { |f| File.exists?(f) }
154
+ raise "built files not found: #{notfound.join(",")}" unless notfound.count == 0
155
+ @attributes.artifact_list = list
156
+ @attributes.artifact_list << Volley.config.volleyfile
157
+ end
158
+
159
+ if @attributes.pack
160
+ action :pack, :post do
161
+ path = @attributes.pack_dir = "/var/tmp/volley-#{Time.now.to_i}-#{$$}"
162
+ Dir.mkdir(path)
163
+ dir = Dir.pwd
164
+
165
+ @attributes.artifact_list.each do |art|
166
+ if art =~ /^\// && art !~ /^#{dir}/
167
+ # file is full path and not in current directory
168
+ source = art
169
+ dest = "#{path}/#{File.basename(art)}"
170
+ else
171
+ # file is relative path or in current directory
172
+ f = art.gsub(/^#{dir}/, "").gsub(/^\//, "")
173
+ source = "#{dir}/#{f}"
174
+ dest = "#{path}/#{f}"
175
+ end
176
+
177
+ begin
178
+ Volley::Log.debug "pack file: #{source} => #{dest}"
179
+ FileUtils.mkdir_p(File.dirname(dest))
180
+ if File.directory?(source)
181
+ FileUtils.cp_r(source, dest)
182
+ else
183
+ FileUtils.copy(source, dest)
184
+ end
185
+ rescue => e
186
+ raise "could not copy file #{source}: #{e.message}"
187
+ end
188
+ end
189
+
190
+ origpath = Dir.pwd
191
+ Dir.chdir(path)
192
+ case @attributes.pack_type
193
+ when "tgz"
194
+ n = "#{args.branch}-#{args.version}.tgz"
195
+ c = "tar cvfz #{n} *"
196
+ Volley::Log.debug "command:#{c}"
197
+ shellout(c)
198
+
199
+ @attributes.artifact = "#{path}/#{n}"
200
+ else
201
+ raise "unknown pack type '#{@attributes.pack_type}'"
202
+ end
203
+
204
+ Dir.chdir(origpath)
205
+ end
206
+ end
207
+
208
+ if @attributes.encrypt
209
+ action :encrypt, :post do
210
+ art = @attributes.artifact
211
+ key = @attributes.encrypt_key
212
+ cpt = "#{art}.cpt"
213
+
214
+ raise "in action encrypt: artifact file does not exist: #{art}" unless File.file?(art)
215
+ raise "in action encrypt: encrypted file #{cpt} already exists" if File.file?(cpt) && !@attributes.encrypt_overwrite
216
+ shellout("ccrypt -e --key '#{key}' #{art}")
217
+
218
+ @attributes.artifact_unencrypted = art
219
+ @attributes.artifact = cpt
220
+ end
221
+ end
222
+
223
+ action :push, :post do
224
+ publisher = Volley::Dsl.publisher
225
+ publisher.push(@project.name, args.branch, args.version, @attributes.artifact)
226
+ end
227
+ end
228
+
229
+ def pull
230
+ action :download do
231
+ pr = @project.name
232
+ br = args.branch
233
+ ve = args.version
234
+
235
+ pub = Volley::Dsl.publisher
236
+ file = pub.pull(pr, br, ve)
237
+
238
+ dir = File.dirname(file)
239
+ Volley::Log.info "changing directory: #{dir} (#{file})"
240
+ cmd = "volley run #{pr}:#{plan} branch:#{branch} #{arg_list.join(' ')}"
241
+
242
+ Volley::Log.info "command: #{cmd}"
243
+ FileUtils.mkdir_p("#{dir}/unpack")
244
+ Dir.chdir("#{dir}/unpack")
245
+ tgz = %x{tar xvfz #{file} 2>/dev/null}
246
+ File.open("#{dir}/tgz.log", "w") {|f| f.write(tgz)}
247
+ end
248
+ end
249
+
250
+ #def volley(opts={ })
251
+ # o = {
252
+ # :project => @project.name,
253
+ # :branch => args.branch,
254
+ # :version => "latest",
255
+ # :plan => "pull",
256
+ # }.merge(opts)
257
+ #
258
+ # desc = [o[:project], o[:branch], o[:version], o[:plan]].compact.join(":")
259
+ # actionname = "volley-#{desc}"
260
+ # action actionname do
261
+ # Volley::Log.info "VOLLEY: #{desc}"
262
+ # cmd = ["volley"]
263
+ # cmd << desc
264
+ # shellout(cmd.join(" "), :output => true)
265
+ # end
266
+ #end
267
+
268
+ def volley(opts={ })
269
+ o = {
270
+ :project => @project.name,
271
+ #:branch => args.branch,
272
+ #:version => "latest",
273
+ :plan => "pull",
274
+ }.merge(opts)
275
+
276
+ pr = o[:project]
277
+ pl = o[:plan]
278
+
279
+ action "volley-#{pr}-#{pl}" do
280
+ plan = Volley::Dsl.project(pr).plan(pl)
281
+ plan.call(:rawargs => @rawargs)
282
+ end
283
+
284
+ #desc = [o[:project], o[:branch], o[:version], o[:plan]].compact.join(":")
285
+ #actionname = "volley-#{desc}"
286
+ #action actionname do
287
+ # Volley::Log.info "VOLLEY: #{desc}"
288
+ # cmd = ["volley"]
289
+ # cmd << desc
290
+ # shellout(cmd.join(" "), :output => true)
291
+ #end
292
+ end
293
+
294
+ def command(*args)
295
+ name = args.join(" ").parameterize.to_sym
296
+ action name do
297
+ shellout(*args)
298
+ end
299
+ end
300
+
301
+ def shellout(*args)
302
+ require "mixlib/shellout"
303
+ opts = args.last.is_a?(Hash) ? args.pop : {}
304
+ options = {
305
+ :output => @attributes.output,
306
+ :prepend => ">> ",
307
+ }.merge(opts)
308
+ command = ::Mixlib::ShellOut.new(*args)
309
+ command.run_command
310
+ command.stdout.lines.each { |l| Volley::Log.info "#{options[:prepend]}#{l}" } if options[:output] && command.stdout
311
+ command.stderr.lines.each { |l| Volley::Log.info "#{options[:prepend]}#{l}" } if options[:output] && command.stderr
312
+ command.error!
313
+ { :out => command.stdout, :err => command.stderr }
314
+ end
315
+
316
+ private
317
+
318
+ def boolean(value)
319
+ case value.class
320
+ when TrueClass, FalseClass
321
+ return value
322
+ else
323
+ return true if value =~ /^(1|t|true|y|yes)$/
324
+ return false if value =~ /^(0|f|false|n|no)$/
325
+ end
326
+ nil
327
+ end
328
+
329
+ def process_arguments(raw)
330
+ if raw
331
+ kvs = raw.select{|e| e =~ /\:/}
332
+ raw = raw.reject{|e| e =~ /\:/}
333
+ @rawargs = raw
334
+ @argdata = kvs.inject({ }) { |h, a| (k, v) = a.split(/:/); h[k.to_sym]= v; h }
335
+ end
336
+ end
337
+ end
338
+ end
339
+ end