volley 0.1.0.alpha4 → 0.1.0.alpha5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +3 -1
  2. data/.rspec +1 -0
  3. data/Gemfile +4 -1
  4. data/Rakefile +34 -0
  5. data/bin/volley +88 -40
  6. data/conf/common.volleyfile +12 -29
  7. data/features/publisher.feature +45 -0
  8. data/features/step_definitions/common_steps.rb +9 -0
  9. data/features/step_definitions/publisher_steps.rb +92 -0
  10. data/features/support/env.rb +19 -0
  11. data/init/Volleyfile +1 -104
  12. data/lib/volley/descriptor.rb +28 -0
  13. data/lib/volley/dsl/action.rb +31 -0
  14. data/lib/volley/dsl/argument.rb +96 -0
  15. data/lib/volley/dsl/file.rb +70 -0
  16. data/lib/volley/dsl/plan.rb +110 -235
  17. data/lib/volley/dsl/project.rb +10 -2
  18. data/lib/volley/dsl/pull_action.rb +50 -0
  19. data/lib/volley/dsl/push_action.rb +101 -0
  20. data/lib/volley/dsl/stage.rb +40 -0
  21. data/lib/volley/dsl.rb +7 -0
  22. data/lib/volley/log.rb +22 -8
  23. data/lib/volley/meta.rb +24 -0
  24. data/lib/volley/publisher/amazons3.rb +67 -66
  25. data/lib/volley/publisher/base.rb +81 -42
  26. data/lib/volley/publisher/exceptions.rb +7 -0
  27. data/lib/volley/publisher/local.rb +41 -27
  28. data/lib/volley/scm/base.rb +10 -0
  29. data/lib/volley.rb +38 -12
  30. data/spec/descriptor_spec.rb +39 -0
  31. data/spec/dsl_plan_spec.rb +103 -0
  32. data/spec/dsl_project_spec.rb +36 -0
  33. data/spec/dsl_volleyfile_spec.rb +21 -0
  34. data/spec/meta_spec.rb +26 -0
  35. data/spec/publisher_spec.rb +92 -0
  36. data/test/dsl/amazons3_publisher.volleyfile +6 -0
  37. data/test/dsl/local_publisher.volleyfile +4 -0
  38. data/test/dsl/log_console.volleyfile +2 -0
  39. data/test/dsl/log_file.volleyfile +2 -0
  40. data/test/dsl/simple.volleyfile +17 -0
  41. data/test/meta.yml +3 -0
  42. data/test/project/Rakefile +13 -0
  43. data/test/project/Volleyfile +18 -0
  44. data/test/trunk-1.tgz +0 -0
  45. data/volley.gemspec +2 -1
  46. metadata +67 -5
  47. data/lib/volley/config.rb +0 -8
  48. data/lib/volley/volley_file.rb +0 -45
data/.gitignore CHANGED
@@ -8,9 +8,11 @@ InstalledFiles
8
8
  _yardoc
9
9
  coverage
10
10
  doc/
11
+ log/
11
12
  lib/bundler/man
12
13
  pkg
13
14
  rdoc
14
15
  spec/reports
15
- test/*
16
+ test/publisher
17
+ test/project/file
16
18
  tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --format s -c
data/Gemfile CHANGED
@@ -4,4 +4,7 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'rake'
7
- gem 'templar'
7
+ gem 'cucumber'
8
+ gem 'rspec'
9
+
10
+ gem 'awesome_print'
data/Rakefile CHANGED
@@ -1,2 +1,36 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+ require 'cucumber/rake/task'
5
+
6
+ $:.unshift File.expand_path("lib/")
7
+ require 'volley'
8
+
9
+ desc 'Default: run specs.'
10
+ task :default => :test
11
+
12
+ desc "Run specs"
13
+ RSpec::Core::RakeTask.new(:spec) do |t|
14
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
15
+ # Put spec opts in a file named .rspec in root
16
+ end
17
+
18
+ desc "Run Cucumber"
19
+ Cucumber::Rake::Task.new do |t|
20
+ t.cucumber_opts = %w{--format pretty --no-snippets}
21
+ end
22
+
23
+ desc "Run RSpec and Cucumber tests"
24
+ task :test do
25
+ begin
26
+ Rake::Task["spec"].invoke
27
+ rescue => e
28
+ puts "#{e.message} at #{e.backtrace.first}"
29
+ end
30
+
31
+ begin
32
+ Rake::Task["cucumber"].invoke
33
+ rescue => e
34
+ puts "#{e.message} at #{e.backtrace.first}"
35
+ end
36
+ end
data/bin/volley CHANGED
@@ -1,55 +1,103 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'rubygems'
4
+ require 'docopt'
4
5
  require 'volley'
5
- require 'clamp'
6
+ require 'awesome_print'
6
7
 
7
- Volley::VolleyFile.init
8
- STDOUT.sync = true
8
+ DOC = <<-DOC
9
+ Usage: volley [options] <plan> <descriptor> [argument]...
10
+
11
+ <plan>
12
+ The plan to run.
13
+ reserved plan names: [versions, latest, list]
14
+
15
+ <descriptor>
16
+ A descriptor should conform to the following format:
17
+ <project>[@<branch>[:<version>]]
18
+
19
+ project: the project name.
20
+ branch: the branch name.
21
+ Defaults to the branch currently in use,
22
+ must be specified for remote mode.
23
+ version: the version (revision)
24
+ Defaults to the current revision
25
+ In remote mode, defaults to "latest"
26
+
27
+ <argument>
28
+ A list of key=value pairs
29
+
30
+ Options:
31
+ -h --help show this help message and exit
32
+ --version show version and exit
33
+ -d --debug show debug output, change log level to debug
34
+ -c --config FILE load additional Volleyfile [default: ~/.Volleyfile]
35
+ -p --primary FILE load primary Volleyfile [default: ./Volleyfile]
36
+
37
+ -f --fork fork process into background and exit
38
+ -l --log LOG log file [default: /opt/volley/volley.log]
39
+ -L --level LEVEL log level [default: debug]
40
+ DOC
9
41
 
10
42
  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(",")}"
43
+ class Command
44
+ def initialize
45
+ end
46
+
47
+ def run(argv)
48
+ STDOUT.sync = true
49
+ options = Docopt(DOC, Volley::Version::STRING)
50
+ debug = options[:debug]
51
+ config = options[:config]
52
+ primary = options[:primary]
53
+ fork = options[:fork]
54
+ log = options[:log]
55
+ level = options[:level]
56
+
57
+ Volley::Dsl::VolleyFile.init
58
+ Volley::Dsl::VolleyFile.load(config, :optional => true)
59
+ Volley::Dsl::VolleyFile.load(primary, :primary => true) if File.file?(primary)
60
+ Volley::Log.add(level.to_sym, log)
61
+ Volley::Log.console_debug if debug
62
+ Volley.config.debug = debug
63
+
64
+ kvs = argv.select { |e| e.match(/(\w+)\=(\w+)/) }
65
+ pos = argv.reject { |e| e.match(/(\w+)\=(\w+)/) }
66
+
67
+ plan = pos.shift
68
+ desc = pos.shift
69
+
70
+ raise "must specify plan" unless plan
71
+
72
+ if Volley::Dsl.project(:volley).plan?(plan)
73
+ # the plan is reserved
74
+ plan = "volley:#{plan}"
75
+ else
76
+ # the plan isn't reserved
77
+ raise "must specify descriptor" unless desc
78
+ (project, branch, version) = Volley::Descriptor.new(desc).get
79
+ plan = "#{project}:#{plan}"
38
80
  end
39
81
 
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)
82
+ if debug
83
+ Volley::Log.debug "## OPTIONS ##"
84
+ Volley::Log.debug "plan: #{plan}"
85
+ Volley::Log.debug "descriptor: #{desc}"
86
+ Volley::Log.debug "positional: #{pos.join(",")}"
87
+ Volley::Log.debug "key:value: #{kvs.join(",")}"
88
+ end
42
89
 
43
- Volley.process(:project => project, :plan => plan, :branch => branch, :version => version, :args => args)
44
- rescue Interrupt => e
45
- Volley::Log.warn "Cancelled..."
90
+ Volley::Log.info "processing '#{plan}' plan for '#{desc}'"
91
+ Volley.process(:plan => plan, :descriptor => desc, :args => kvs)
46
92
  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
93
+ Volley::Log.error "exception: #{e.message} at #{e.backtrace.first}"
94
+ if debug
95
+ Volley::Log.error e
96
+ else
97
+ Volley::Log.debug e
98
+ end
51
99
  end
52
100
  end
53
101
  end
54
102
 
55
- Volley::Command.run
103
+ Volley::Command.new.run(ARGV)
@@ -1,13 +1,14 @@
1
1
  project :volley do
2
- plan :init do
2
+ scm :base
3
+ plan :init, :remote => false do
3
4
  default do
4
- file = File.expand_path("../init/Volleyfile", __FILE__)
5
+ file = File.expand_path("../../init/Volleyfile", __FILE__)
5
6
  dest = "#{Dir.pwd}/Volleyfile"
6
7
  FileUtils.copy(file, dest)
7
8
  puts "created: #{dest}"
8
9
  end
9
10
  end
10
- plan :list do
11
+ plan :list, :remote => false do
11
12
  default do
12
13
  Volley::Dsl::Project.projects.each do |p, project|
13
14
  Volley::Log.info "project: #{p}"
@@ -18,40 +19,24 @@ project :volley do
18
19
  end
19
20
  end
20
21
  plan :latest do
21
- argument :project #, :required => true
22
- argument :branch #, :required => true
23
22
  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
23
+ ap args
24
+ (project,branch,version) = args.descriptor.get
35
25
  raise "project and branch must be specified" unless project && branch
36
26
 
37
27
  pub = Volley::Dsl.publisher
38
28
  puts pub.latest(project, branch)
39
29
  end
40
30
  end
41
- plan :versions do
42
- argument :project
43
- argument :branch
44
- argument :version
31
+ plan :versions, :remote => false do
45
32
  argument :all, :convert => :boolean, :default => false
46
33
  argument :output, :default => "list", :convert => :to_sym, :choices => ["json", "xml", "list"]
47
34
 
48
35
  default do
49
- project = args.project
50
- branch = args.branch
51
- version = args.version
36
+ (project, branch, version) = args.descriptor.get
52
37
 
53
- if project.nil? && rawargs
54
- first = rawargs.first
38
+ if project.nil? && argv.count
39
+ first = argv.first
55
40
  (p, b, v) = first.split(/\//) if first
56
41
  if p
57
42
  project = p
@@ -68,7 +53,7 @@ project :volley do
68
53
  else
69
54
  if project
70
55
  if branch
71
- if version
56
+ if version != "latest"
72
57
  data = pub.contents(project, branch, version)
73
58
  else
74
59
  data = pub.versions(project, branch)
@@ -92,11 +77,9 @@ project :volley do
92
77
  end
93
78
  end
94
79
  plan :remote do
95
- argument :version, :required => true
96
-
97
80
  default do
98
81
  pub = Volley::Dsl.publisher
99
- (pr, br, vr) = args.version.split(/[\:\/\.]/)
82
+ (pr, br, vr) = args.descriptor.get
100
83
  vr ||= 'latest'
101
84
  vf = pub.volleyfile(:project => pr, :branch => br, :version => vr)
102
85
  load vf
@@ -0,0 +1,45 @@
1
+
2
+ # Built these tests as a starting point
3
+ # duplicates some of the tests in spec/publisher_spec
4
+ Feature: Publisher
5
+ In order to store artifacts
6
+ I want to be able to manage files with a publisher
7
+
8
+ Background:
9
+ Given I have a publisher with an empty repository
10
+
11
+ Scenario: Empty publisher
12
+ When I request a list of projects from the publisher
13
+ Then I should see an empty list
14
+
15
+ Scenario: Publish multiple artifacts
16
+ When I publish the artifact test/trunk/1
17
+ Then there should be 1 projects in the publisher
18
+ And the test project should have 1 branches
19
+ When I publish the artifact test/staging/2
20
+ Then there should be 1 projects in the publisher
21
+ And the test project should have 2 branches
22
+
23
+ Scenario: Publish and retrieve artifact
24
+ When I publish the artifact test/trunk/1
25
+ Then there should be 1 projects in the publisher
26
+ And the test project should have 1 branches
27
+ When I deploy the artifact test/trunk/1
28
+ Then I should not receive an exception
29
+
30
+ Scenario: Latest artifacts
31
+ Given I have a populated repository
32
+ Then the test project should have 3 branches
33
+ And the latest of test/trunk should be 7
34
+ And the latest of test/staging should be 5
35
+
36
+ Scenario: Duplicate Artifact
37
+ Given I have a populated repository
38
+ When I publish a duplicate artifact test/staging/12
39
+ Then I should receive an exception
40
+
41
+ # TODO: make this work
42
+ # Scenario: Duplicate Artifact force
43
+ # Given I have a populated repository
44
+ # When I force publish a duplicate artifact test/staging/12
45
+ # Then I should not receive an exception
@@ -0,0 +1,9 @@
1
+
2
+
3
+ Then /^I should receive an exception$/ do
4
+ fail if @exception.nil?
5
+ end
6
+
7
+ Then /^I should not receive an exception$/ do
8
+ fail unless @exception.nil?
9
+ end
@@ -0,0 +1,92 @@
1
+ def publish(desc)
2
+ pwd = Dir.pwd
3
+ Dir.chdir("test/project")
4
+ Volley::Dsl::VolleyFile.load("Volleyfile")
5
+ Volley.process(:plan => "publish", :descriptor => desc)
6
+ Dir.chdir(pwd)
7
+ end
8
+
9
+ def deploy(desc)
10
+ pwd = Dir.pwd
11
+ Volley.process(:plan => "deploy", :descriptor => desc)
12
+ Dir.chdir(pwd)
13
+ end
14
+
15
+ Given /^I have a publisher with an empty repository$/ do
16
+ @root ||= Volley.config.project_root
17
+ %w{local remote}.each { |d| FileUtils.rm_rf("#@root/test/publisher/#{d}") }
18
+ %w{local remote}.each { |d| FileUtils.mkdir_p("#@root/test/publisher/#{d}") }
19
+ Volley::Dsl::VolleyFile.load("#@root/test/dsl/local_publisher.volleyfile")
20
+ @pub = Volley::Dsl.publisher
21
+ end
22
+
23
+ Given /^I have a populated repository$/ do
24
+ steps %Q{
25
+ When I publish the artifact test/trunk/1
26
+ And I publish the artifact test/staging/2
27
+ And I publish the artifact test/trunk/3
28
+ And I publish the artifact test/master/4
29
+ And I publish the artifact test/staging/5
30
+ And I publish the artifact test/trunk/7
31
+ }
32
+ end
33
+
34
+ When /^I request a list of projects from the publisher$/ do
35
+ @emptylist = @pub.projects
36
+ end
37
+
38
+ Then /^I should see an empty list$/ do
39
+ fail unless @emptylist.count == 0
40
+ end
41
+
42
+ When /^I publish the artifact (.*)$/ do |desc|
43
+ publish(desc)
44
+ end
45
+
46
+ When /^I deploy the artifact (.*)$/ do |desc|
47
+ begin
48
+ deploy(desc)
49
+ rescue => e
50
+ @exception = e
51
+ end
52
+ end
53
+
54
+ When /^I publish a duplicate artifact (.*)$/ do |desc|
55
+ steps %Q{
56
+ When I publish the artifact #{desc}
57
+ }
58
+ begin
59
+ publish(desc)
60
+ rescue => e
61
+ @exception = e
62
+ end
63
+ end
64
+
65
+ # TODO: make this work
66
+ When /^I force publish a duplicate artifact (.*)$/ do |desc|
67
+ steps %Q{
68
+ When I publish the artifact #{desc}
69
+ }
70
+ begin
71
+ publish(desc)
72
+ rescue => e
73
+ puts "EXCEPTION"
74
+ @exception = e
75
+ end
76
+ end
77
+
78
+ Then /^there should be (.*) projects in the publisher$/ do |count|
79
+ list = @pub.projects
80
+ #puts "LIST:#{list.inspect}"
81
+ fail unless list.count == count.to_i
82
+ end
83
+
84
+ Then /^the (.*) project should have (.*) branches$/ do |project, count|
85
+ list = @pub.branches(project)
86
+ fail unless list.count == count.to_i
87
+ end
88
+
89
+ Then /^the latest of (.*)\/(.*) should be (.*)$/ do |project, branch, version|
90
+ latest = @pub.latest(project, branch)
91
+ fail unless latest == "#{project}/#{branch}/#{version}"
92
+ end
@@ -0,0 +1,19 @@
1
+ $:.unshift File.expand_path("lib/")
2
+
3
+ require 'fileutils'
4
+ require 'volley'
5
+
6
+ root = Dir.pwd
7
+
8
+ Volley::Log.add(:debug, "#{Dir.pwd}/log/volley.log")
9
+ Volley::Log.console_disable
10
+ Volley.config.project_root = root
11
+
12
+ World do
13
+ @root = root
14
+ end
15
+
16
+ at_exit do
17
+ puts "cleaning up... #{root}"
18
+ %w{local remote}.each { |d| FileUtils.rm_rf("#{root}/test/publisher/#{d}") }
19
+ end
data/init/Volleyfile CHANGED
@@ -1,104 +1 @@
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
1
+ # TODO: write this
@@ -0,0 +1,28 @@
1
+
2
+ module Volley
3
+ class Descriptor
4
+ attr_reader :project, :branch, :version
5
+
6
+ def initialize(desc="", options={})
7
+ @options = {
8
+ :partial => false,
9
+ }.merge(options)
10
+
11
+ if desc
12
+ list = desc.split(/[\@\:\.\/\\\-]/)
13
+ raise "error parsing descriptor: #{desc}" if (list.count < 2 || list.count > 3) && !@options[:partial]
14
+ (@project, @branch, @version) = list
15
+ @version ||= "latest"
16
+ raise "error parsing descriptor: #{desc}" unless (@project && @branch && @version) || @options[:partial]
17
+ end
18
+ end
19
+
20
+ def get
21
+ [@project, @branch, @version]
22
+ end
23
+
24
+ def ==(other)
25
+ @project == other.project && @branch == other.branch && @version == other.version
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+
2
+ module Volley
3
+ module Dsl
4
+ class Action
5
+ attr_reader :plan
6
+
7
+ def initialize(name, options={}, &block)
8
+ @name = name.to_sym
9
+ @stage = options.delete(:stage)
10
+ @plan = options.delete(:plan)
11
+ @block = block
12
+ @options = {
13
+ }.merge(options)
14
+ raise "stage instance must be set" unless @stage
15
+ raise "plan instance must be set" unless @plan
16
+ end
17
+
18
+ def call
19
+ Volley::Log.debug ".. .. #@name"
20
+ self.instance_eval &@block if @block
21
+ end
22
+
23
+ delegate :project, :args, :files, :file, :attributes, :log, :arguments, :argv, :branch, :version, :action,
24
+ :to => :plan
25
+
26
+ def command(cmd)
27
+ @plan.shellout(cmd)
28
+ end
29
+ end
30
+ end
31
+ end