volley 0.1.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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