engineyard-hudson 0.1.0

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.
@@ -0,0 +1,5 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .DS_Store
5
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in engineyard-hudson.gemspec
4
+ gemspec
@@ -0,0 +1,47 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ engineyard-hudson (0.0.1)
5
+ thor (~> 0.14.3)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ awesome_print (0.2.1)
11
+ builder (2.1.2)
12
+ cucumber (0.9.3)
13
+ builder (~> 2.1.2)
14
+ diff-lcs (~> 1.1.2)
15
+ gherkin (~> 2.2.9)
16
+ json (~> 1.4.6)
17
+ term-ansicolor (~> 1.0.5)
18
+ diff-lcs (1.1.2)
19
+ gherkin (2.2.9)
20
+ json (~> 1.4.6)
21
+ term-ansicolor (~> 1.0.5)
22
+ json (1.4.6)
23
+ rake (0.8.7)
24
+ rspec (2.0.1)
25
+ rspec-core (~> 2.0.1)
26
+ rspec-expectations (~> 2.0.1)
27
+ rspec-mocks (~> 2.0.1)
28
+ rspec-core (2.0.1)
29
+ rspec-expectations (2.0.1)
30
+ diff-lcs (>= 1.1.2)
31
+ rspec-mocks (2.0.1)
32
+ rspec-core (~> 2.0.1)
33
+ rspec-expectations (~> 2.0.1)
34
+ term-ansicolor (1.0.5)
35
+ thor (0.14.3)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ awesome_print
42
+ cucumber (~> 0.9.3)
43
+ engineyard-hudson!
44
+ json (~> 1.4.0)
45
+ rake (~> 0.8.7)
46
+ rspec (~> 2.0.1)
47
+ thor (~> 0.14.3)
@@ -0,0 +1,6 @@
1
+ # History
2
+
3
+ ## 0.1.0 - 2010-10-30
4
+
5
+ * Initial 'ey-hudson install .' command
6
+ * 'ey-hudson server' shows 'Coming soon'
@@ -0,0 +1,74 @@
1
+ # Easier to do CI than not to.
2
+
3
+ Run your continuous integration (CI) tests against your Engine Yard AppCloud environments - the exact same configuration you are using in production!
4
+
5
+ You're developing on OS X or Windows, deploying to Engine Yard AppCloud (Gentoo/Linux), and you're running your CI on your local machine or a spare Ubuntu machine in the corner of the office, or ... you're not running CI at all?
6
+
7
+ It's a nightmare. It was for me. [Hudson CI](http://hudson-ci.org/), `engineyard-hudson` and the [hudson](http://github.com/cowboyd/hudson.rb) CLI projects now make CI easier to do than not to. A few commands and your Rails applications' tests are automatically running, no additional setup, and its the same environment you are deploying your Rails applications (Engine Yard AppCloud). Sweet.
8
+
9
+ ## Installation
10
+
11
+ gem install engineyard-hudson
12
+
13
+ ## Assumptions
14
+
15
+ It is assumed you are familiar with the `engineyard` CLI gem.
16
+
17
+ You **do not** need to be familiar with custom chef recipes. Just follow the simple commands. Easy peasy.
18
+
19
+ ## Warning
20
+
21
+ In the very first release of `engineyard-hudson`, there is no support for authentication/authorization of Hudson CI.
22
+
23
+ ## Hosting Hudson CI on Engine Yard AppCloud
24
+
25
+ Instructions *coming soon*.
26
+
27
+ Hosting Hudson CI on Engine Yard AppCloud is optional. It can be hosted anywhere. You need the following information about your Hudson CI:
28
+
29
+ * Hudson CI host (& port)
30
+ * Hudson CI's user's public key (probably at /home/hudson/.ssh/id_rsa.pub)
31
+ * Hudson CI's user's private key path (probably /home/hudson/.ssh/id_rsa)
32
+
33
+ ## Running your tests in Hudson against Engine Yard AppCloud
34
+
35
+ After a couple manual steps, you will have your applications tests running.
36
+
37
+ $ cd /my/project
38
+ $ ey-hudson install .
39
+
40
+ Finally:
41
+ * edit cookbooks/hudson_slave/attributes/default.rb as necessary.
42
+ * run: ey recipes upload # use --environment(-e) & --account(-c)
43
+ * run: ey recipes apply # to select environment
44
+ * Boot your environment if not already booted.
45
+
46
+ Done! Either visit your Hudson CI site or use `hudson list` to see the status of your projects being tested.
47
+
48
+ ## Automatically triggering job builds
49
+
50
+ In Hudson CI, a "job" is one of your projects. Each time it runs your tests, it is called a "build".
51
+
52
+ It is often desirable to have your SCM trigger Hudson CI to run your job build whenever you push new code.
53
+
54
+ ### GitHub Service Hooks
55
+
56
+ * Go to the "Admin" section of your GitHub project
57
+ * Click "Service Hooks"
58
+ * Click "Post-Receive URLs"
59
+ * Enter the URL `http://HUDSON-CI-URL/job/APP-NAME/build`
60
+ * Click "Update Settings"
61
+
62
+ You can also use the "Test Hook" link to test this is wired up correctly.
63
+
64
+ ### CLI
65
+
66
+ Using the `hudson` CLI:
67
+
68
+ hudson build APP-NAME
69
+
70
+ ### Curl
71
+
72
+ You are triggering the build via a GET call to an URL endpoint. So you can also use `curl`:
73
+
74
+ curl http://HUDSON-CI-URL/job/APP-NAME/build
@@ -0,0 +1,19 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ namespace :cucumber do
5
+ require 'cucumber/rake/task'
6
+ Cucumber::Rake::Task.new(:wip, 'Run features that are being worked on') do |t|
7
+ t.cucumber_opts = "--tags @wip"
8
+ end
9
+ Cucumber::Rake::Task.new(:ok, 'Run features that should be working') do |t|
10
+ t.cucumber_opts = "--tags ~@wip"
11
+ end
12
+ task :all => [:ok, :wip]
13
+ end
14
+
15
+ desc 'Alias for cucumber:ok'
16
+ task :cucumber => 'cucumber:ok'
17
+
18
+ desc "Start test server; Run cucumber:ok; Kill Test Server;"
19
+ task :default => ["cucumber"]
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
4
+ require 'engineyard-hudson'
5
+ require 'engineyard-hudson/cli'
6
+
7
+ Engineyard::Hudson::CLI.start
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "engineyard-hudson"
5
+ s.version = '0.1.0'
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ["Dr Nic Williams"]
8
+ s.email = ["drnicwilliams@gmail.com"]
9
+ s.homepage = "http://github.com/engineyard/engineyard-hudson"
10
+ s.summary = %q{Easier to do CI than not to. Use Hudson CI on Engine Yard AppCloud.}
11
+ s.description = %q{Run your continuous integration (CI) tests against your Engine Yard AppCloud environments - the exact same configuration you are using in production!}
12
+
13
+ s.rubyforge_project = "engineyard-hudson"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency("thor", ["~> 0.14.3"])
21
+ s.add_dependency("engineyard", ["~> 1.3.3"])
22
+ # s.add_dependency("hudson", ["~> 0.3.0.beta"])
23
+
24
+ s.add_development_dependency("rake", ["~> 0.8.7"])
25
+ s.add_development_dependency("cucumber", ["~> 0.9.3"])
26
+ s.add_development_dependency("rspec", ["~> 2.0.1"])
27
+ s.add_development_dependency("json", ["~>1.4.0"])
28
+ s.add_development_dependency("awesome_print")
29
+ end
@@ -0,0 +1,57 @@
1
+ Feature: Managing a rails project as a Hudson CI job on AppCloud
2
+ I want to build/test my project in the same environment I run in Engine Yard AppCloud
3
+
4
+ Scenario: Setup first project as a slave for Hudson
5
+ Given I have an environment "hudson" on account "drnic" on AppCloud
6
+ And I have an environment "my_app_ci" on account "drnic" on AppCloud
7
+ And I am in the "rails" project folder
8
+ When I run local executable "ey-hudson" with arguments "install ."
9
+ Then file "cookbooks/hudson_slave/attributes/default.rb" is created
10
+ And file "cookbooks/hudson_slave/recipes/default.rb" is created
11
+ And file "cookbooks/main/recipes/default.rb" is created
12
+ And file "cookbooks/main/libraries/ruby_block.rb" is created
13
+ And I should see exactly
14
+ """
15
+ create cookbooks
16
+ create cookbooks/main/attributes/recipe.rb
17
+ create cookbooks/main/definitions/ey_cloud_report.rb
18
+ create cookbooks/main/libraries/ruby_block.rb
19
+ create cookbooks/main/libraries/run_for_app.rb
20
+ create cookbooks/hudson_slave/attributes/default.rb
21
+ create cookbooks/hudson_slave/recipes/default.rb
22
+ create cookbooks/main/recipes/default.rb
23
+
24
+ Finally:
25
+ * edit cookbooks/hudson_slave/attributes/default.rb as necessary.
26
+ * run: ey recipes upload # use --environment(-e) & --account(-c)
27
+ * run: ey recipes apply # to select environment
28
+ * Boot your environment if not already booted.
29
+ When the recipe completes, your project will commence its first build on Hudson CI.
30
+ """
31
+
32
+ Scenario: Setup project with existing cookbooks as a slave for Hudson
33
+ Given I have an environment "hudson" on account "drnic" on AppCloud
34
+ And I have an environment "my_app_ci" on account "drnic" on AppCloud
35
+ And I am in the "rails" project folder
36
+ And I already have cookbooks installed
37
+ When I run local executable "ey-hudson" with arguments "install ."
38
+ Then file "cookbooks/hudson_slave/attributes/default.rb" is created
39
+ And file "cookbooks/hudson_slave/recipes/default.rb" is created
40
+ And file "cookbooks/main/recipes/default.rb" is created
41
+ And file "cookbooks/redis/recipes/default.rb" is created
42
+ And I should see exactly
43
+ """
44
+ create cookbooks/hudson_slave/attributes/default.rb
45
+ create cookbooks/hudson_slave/recipes/default.rb
46
+ append cookbooks/main/recipes/default.rb
47
+
48
+ Finally:
49
+ * edit cookbooks/hudson_slave/attributes/default.rb as necessary.
50
+ * run: ey recipes upload # use --environment(-e) & --account(-c)
51
+ * run: ey recipes apply # to select environment
52
+ * Boot your environment if not already booted.
53
+ When the recipe completes, your project will commence its first build on Hudson CI.
54
+ """
55
+
56
+
57
+
@@ -0,0 +1,12 @@
1
+ @wip
2
+ Feature: Managing ey hudson server
3
+ I want to setup and manage a Hudson CI server hosted on Engine Yard AppCloud
4
+
5
+ Scenario: Setup new Hudson CI server on AppCloud
6
+ Given I have an environment "hudson" on account "drnic" on AppCloud
7
+ And I am in the "rails" project folder
8
+ When I run local executable "ey-hudson" with arguments "server -e hudson -c drnic"
9
+ Then I should see "Hudson CI server recipe installed."
10
+
11
+
12
+
@@ -0,0 +1,191 @@
1
+ Given /^this project is active project folder/ do
2
+ @active_project_folder = File.expand_path(File.dirname(__FILE__) + "/../..")
3
+ end
4
+
5
+ Given /^env variable \$([\w_]+) set to( project path|) "(.*)"/ do |env_var, path, value|
6
+ in_project_folder {
7
+ value = File.expand_path(value)
8
+ } unless path.empty?
9
+ ENV[env_var] = value
10
+ end
11
+
12
+ Given /"(.*)" folder is deleted/ do |folder|
13
+ in_project_folder { FileUtils.rm_rf folder }
14
+ end
15
+
16
+ When /^I invoke "(.*)" generator with arguments "(.*)"$/ do |generator, arguments|
17
+ @stdout = StringIO.new
18
+ in_project_folder do
19
+ if Object.const_defined?("APP_ROOT")
20
+ APP_ROOT.replace(FileUtils.pwd)
21
+ else
22
+ APP_ROOT = FileUtils.pwd
23
+ end
24
+ run_generator(generator, arguments.split(' '), SOURCES, :stdout => @stdout)
25
+ end
26
+ File.open(File.join(@tmp_root, "generator.out"), "w") do |f|
27
+ @stdout.rewind
28
+ f << @stdout.read
29
+ end
30
+ end
31
+
32
+ When /^I run executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
33
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
34
+ in_project_folder do
35
+ system "#{executable.inspect} #{arguments} > #{@stdout.inspect} 2> #{@stdout.inspect}"
36
+ end
37
+ end
38
+
39
+ When /^I run project executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
40
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
41
+ in_project_folder do
42
+ system "ruby -rubygems #{executable.inspect} #{arguments} > #{@stdout.inspect} 2> #{@stdout.inspect}"
43
+ end
44
+ end
45
+
46
+ When /^I run local executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
47
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
48
+ executable = File.expand_path(File.join(File.dirname(__FILE__), "/../../bin", executable))
49
+ in_project_folder do
50
+ system "ruby -rubygems #{executable.inspect} #{arguments} > #{@stdout.inspect} 2> #{@stdout.inspect}"
51
+ end
52
+ end
53
+
54
+ When /^I invoke task "rake (.*)"/ do |task|
55
+ @stdout = File.expand_path(File.join(@tmp_root, "tests.out"))
56
+ in_project_folder do
57
+ system "bundle exec rake #{task} --trace > #{@stdout.inspect} 2> #{@stdout.inspect}"
58
+ end
59
+ end
60
+
61
+ Then /^folder "(.*)" (is|is not) created/ do |folder, is|
62
+ in_project_folder do
63
+ File.exists?(folder).should(is == 'is' ? be_true : be_false)
64
+ end
65
+ end
66
+
67
+ Then /^file "(.*)" (is|is not) created/ do |file, is|
68
+ in_project_folder do
69
+ File.exists?(file).should(is == 'is' ? be_true : be_false)
70
+ end
71
+ end
72
+
73
+ Then /^file with name matching "(.*)" is created/ do |pattern|
74
+ in_project_folder do
75
+ Dir[pattern].should_not be_empty
76
+ end
77
+ end
78
+
79
+ Then /^file "(.*)" contents (does|does not) match \/(.*)\// do |file, does, regex|
80
+ in_project_folder do
81
+ actual_output = File.read(file)
82
+ (does == 'does') ?
83
+ actual_output.should(match(/#{regex}/)) :
84
+ actual_output.should_not(match(/#{regex}/))
85
+ end
86
+ end
87
+
88
+ Then /gem file "(.*)" and generated file "(.*)" should be the same/ do |gem_file, project_file|
89
+ File.exists?(gem_file).should be_true
90
+ File.exists?(project_file).should be_true
91
+ gem_file_contents = File.read(File.dirname(__FILE__) + "/../../#{gem_file}")
92
+ project_file_contents = File.read(File.join(@active_project_folder, project_file))
93
+ project_file_contents.should == gem_file_contents
94
+ end
95
+
96
+ Then /^(does|does not) invoke generator "(.*)"$/ do |does_invoke, generator|
97
+ actual_output = get_command_output
98
+ does_invoke == "does" ?
99
+ actual_output.should(match(/dependency\s+#{generator}/)) :
100
+ actual_output.should_not(match(/dependency\s+#{generator}/))
101
+ end
102
+
103
+ Then /help options "(.*)" and "(.*)" are displayed/ do |opt1, opt2|
104
+ actual_output = get_command_output
105
+ actual_output.should match(/#{opt1}/)
106
+ actual_output.should match(/#{opt2}/)
107
+ end
108
+
109
+ Then /^I should see "([^\"]*)"$/ do |text|
110
+ actual_output = get_command_output
111
+ actual_output.should contain(text)
112
+ end
113
+
114
+ Then /^I should not see "([^\"]*)"$/ do |text|
115
+ actual_output =
116
+ actual_output.should_not contain(text)
117
+ end
118
+
119
+ Then /^I should see$/ do |text|
120
+ actual_output = get_command_output
121
+ actual_output.should contain(text)
122
+ end
123
+
124
+ Then /^I should not see$/ do |text|
125
+ actual_output = get_command_output
126
+ actual_output.should_not contain(text)
127
+ end
128
+
129
+ Then /^I should see exactly$/ do |text|
130
+ actual_output = get_command_output
131
+ actual_output.should == text
132
+ end
133
+
134
+ Then /^I should see all (\d+) tests pass/ do |expected_test_count|
135
+ expected = %r{^#{expected_test_count} tests, \d+ assertions, 0 failures, 0 errors}
136
+ actual_output = get_command_output
137
+ actual_output.should match(expected)
138
+ end
139
+
140
+ Then /^I should see all (\d+) examples pass/ do |expected_test_count|
141
+ expected = %r{^#{expected_test_count} examples?, 0 failures}
142
+ actual_output = get_command_output
143
+ actual_output.should match(expected)
144
+ end
145
+
146
+ Then /^yaml file "(.*)" contains (\{.*\})/ do |file, yaml|
147
+ in_project_folder do
148
+ yaml = eval yaml
149
+ YAML.load(File.read(file)).should == yaml
150
+ end
151
+ end
152
+
153
+ Then /^Rakefile can display tasks successfully/ do
154
+ @stdout = File.expand_path(File.join(@tmp_root, "rakefile.out"))
155
+ in_project_folder do
156
+ system "rake -T > #{@stdout.inspect} 2> #{@stdout.inspect}"
157
+ end
158
+ actual_output = get_command_output
159
+ actual_output.should match(/^rake\s+\w+\s+#\s.*/)
160
+ end
161
+
162
+ Then /^task "rake (.*)" is executed successfully/ do |task|
163
+ @stdout.should_not be_nil
164
+ actual_output = get_command_output
165
+ actual_output.should_not match(/^Don't know how to build task '#{task}'/)
166
+ actual_output.should_not match(/Error/i)
167
+ end
168
+
169
+ Then /^gem spec key "(.*)" contains \/(.*)\// do |key, regex|
170
+ in_project_folder do
171
+ gem_file = Dir["pkg/*.gem"].first
172
+ gem_spec = Gem::Specification.from_yaml(`gem spec #{gem_file}`)
173
+ spec_value = gem_spec.send(key.to_sym)
174
+ spec_value.to_s.should match(/#{regex}/)
175
+ end
176
+ end
177
+
178
+ Then /^the file "([^\"]*)" is a valid gemspec$/ do |filename|
179
+ spec = eval(File.read(filename))
180
+ spec.validate
181
+ end
182
+
183
+ When /^I create a new node with the following options on "http:\/\/(.+?):(\d+)":$/ do |host, port, table|
184
+ options = table.raw.inject({}) do |options, (key, value)|
185
+ options[(key.to_sym rescue key) || key] = value
186
+ options
187
+ end
188
+
189
+ Hudson::Api.setup_base_url(:host => host, :port => port.to_i)
190
+ Hudson::Api.add_node(options)
191
+ end
@@ -0,0 +1,14 @@
1
+ Given /^I am in the "([^\"]*)" project folder$/ do |project|
2
+ project_folder = File.expand_path(File.join(@fixtures_path, "projects", project))
3
+ in_tmp_folder do
4
+ FileUtils.cp_r(project_folder, project)
5
+ setup_active_project_folder(project)
6
+ end
7
+ end
8
+
9
+ Given /^I already have cookbooks installed$/ do
10
+ cookbooks_folder = File.expand_path(File.join(@fixtures_path, "cookbooks"))
11
+ in_project_folder do
12
+ FileUtils.cp_r(cookbooks_folder, ".")
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ Given /^I have an environment "([^"]*)" on account "([^"]*)" on AppCloud$/ do |env, account|
2
+
3
+ end
@@ -0,0 +1,37 @@
1
+ module CommonHelpers
2
+ def get_command_output
3
+ strip_color_codes(File.read(@stdout)).chomp
4
+ end
5
+
6
+ def strip_color_codes(text)
7
+ text.gsub(/\e\[\d+m/, '')
8
+ end
9
+
10
+ def in_tmp_folder(&block)
11
+ FileUtils.chdir(@tmp_root, &block)
12
+ end
13
+
14
+ def in_project_folder(&block)
15
+ project_folder = @active_project_folder || @tmp_root
16
+ FileUtils.chdir(project_folder, &block)
17
+ end
18
+
19
+ def in_home_folder(&block)
20
+ FileUtils.chdir(@home_path, &block)
21
+ end
22
+
23
+ def force_local_lib_override(project_name = @project_name)
24
+ rakefile = File.read(File.join(project_name, 'Rakefile'))
25
+ File.open(File.join(project_name, 'Rakefile'), "w+") do |f|
26
+ f << "$:.unshift('#{@lib_path}')\n"
27
+ f << rakefile
28
+ end
29
+ end
30
+
31
+ def setup_active_project_folder project_name
32
+ @active_project_folder = File.join(@tmp_root, project_name)
33
+ @project_name = project_name
34
+ end
35
+ end
36
+
37
+ World(CommonHelpers)
@@ -0,0 +1,13 @@
1
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../../lib'))
2
+ require 'engineyard-hudson'
3
+ require 'bundler/setup'
4
+
5
+ Before do
6
+ @tmp_root = File.dirname(__FILE__) + "/../../tmp"
7
+ @home_path = File.expand_path(File.join(@tmp_root, "home"))
8
+ @lib_path = File.expand_path(File.dirname(__FILE__) + "/../../lib")
9
+ @fixtures_path = File.expand_path(File.dirname(__FILE__) + "/../../fixtures")
10
+ FileUtils.rm_rf @tmp_root
11
+ FileUtils.mkdir_p @home_path
12
+ ENV['HOME'] = @home_path
13
+ end
@@ -0,0 +1,10 @@
1
+
2
+ module Matchers
3
+ RSpec::Matchers.define :contain do |expected_text|
4
+ match do |text|
5
+ text.index expected_text
6
+ end
7
+ end
8
+ end
9
+
10
+ World(Matchers)
@@ -0,0 +1 @@
1
+ require_recipe 'redis'
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.rb"
2
+
3
+ gem "rake"
@@ -0,0 +1,10 @@
1
+ GEM
2
+ remote: http://rubygems.rb/
3
+ specs:
4
+ rake (0.8.7)
5
+
6
+ PLATFORMS
7
+ ruby
8
+
9
+ DEPENDENCIES
10
+ rake
@@ -0,0 +1,4 @@
1
+ desc "Default task runs tests"
2
+ task :default do
3
+ puts "Tests ran successfully!"
4
+ end
@@ -0,0 +1,4 @@
1
+ module Engineyard
2
+ module Hudson
3
+ end
4
+ end
@@ -0,0 +1,44 @@
1
+ require 'thor'
2
+
3
+ module Engineyard
4
+ module Hudson
5
+ class CLI < Thor
6
+
7
+ def self.common_options
8
+ method_option :environment, :type => :string, :aliases => %w(-e),
9
+ :desc => "Environment in which to deploy this application", :required => true
10
+ method_option :account, :type => :string, :aliases => %w(-c),
11
+ :desc => "Name of the account you want to deploy in"
12
+ end
13
+
14
+ desc "install PROJECT_PATH", "Install Hudson node/slave recipes into your project."
15
+ def install(project_path)
16
+ require 'engineyard-hudson/cli/install'
17
+ Engineyard::Hudson::Install.start(ARGV[1..-1])
18
+ end
19
+
20
+ desc "server", "Setup a Hudson CI server on AppCloud."
21
+ def server
22
+ shell.say "Coming soon!", :green
23
+ end
24
+
25
+ desc "version", "show version information"
26
+ def version
27
+ shell.say Engineyard::Hudson::VERSION
28
+ end
29
+
30
+ map "-v" => :version, "--version" => :version, "-h" => :help, "--help" => :help
31
+
32
+ private
33
+ def display(text)
34
+ shell.say text
35
+ exit
36
+ end
37
+
38
+ def error(text)
39
+ shell.say "ERROR: #{text}", :red
40
+ exit
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,62 @@
1
+ require 'thor/group'
2
+
3
+ module Engineyard
4
+ module Hudson
5
+ class Install < Thor::Group
6
+ include Thor::Actions
7
+
8
+ argument :project_path
9
+
10
+ def self.source_root
11
+ File.join(File.dirname(__FILE__), "install", "templates")
12
+ end
13
+
14
+ def install_cookbooks
15
+ file = "cookbooks/main/recipes/default.rb"
16
+ unless File.exists?(File.join(destination_root, "cookbooks/main/recipes/default.rb"))
17
+ directory "cookbooks"
18
+ end
19
+ end
20
+
21
+ def attributes
22
+ template "attributes.rb.erb", "cookbooks/hudson_slave/attributes/default.rb"
23
+ end
24
+
25
+ def recipe
26
+ copy_file "recipes.rb", "cookbooks/hudson_slave/recipes/default.rb"
27
+ end
28
+
29
+ def enable_recipe
30
+ file = "cookbooks/main/recipes/default.rb"
31
+ enable_cmd = "\nrequire_recipe 'hudson_slave'"
32
+ if File.exists?(file_path = File.join(destination_root, file))
33
+ append_file file, enable_cmd
34
+ else
35
+ create_file file, enable_cmd
36
+ end
37
+ end
38
+
39
+ # README:
40
+ # Finally:
41
+ # * edit cookbooks/hudson_slave/attributes/default.rb as necessary
42
+ # * run: ey recipes upload
43
+ # * run: ey recipes apply
44
+ # * Boot your environment if not already booted.
45
+ # When the recipe completes, your project will commence its first build on Hudson CI.
46
+ def readme
47
+ say ""
48
+ say "Finally:"
49
+ say "* edit "; say "cookbooks/hudson_slave/attributes/default.rb ", :yellow; say "as necessary."
50
+ say "* run: "; say "ey recipes upload ", :green; say "# use --environment(-e) & --account(-c)"
51
+ say "* run: "; say "ey recipes apply ", :green; say "# to select environment"
52
+ say "* "; say "Boot your environment ", :yellow; say "if not already booted."
53
+ say "When the recipe completes, your project will commence its first build on Hudson CI."
54
+ end
55
+
56
+ private
57
+ def say(msg, color = nil)
58
+ color ? shell.say(msg, color) : shell.say(msg)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,17 @@
1
+ #
2
+ # Cookbook Name:: hudson_slave
3
+ # Recipe:: default
4
+ #
5
+
6
+ hudson_slave({
7
+ :master => {
8
+ :host => "ec2-174-129-24-134.compute-1.amazonaws.com",
9
+ :port => 80,
10
+ :public_key => "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6AWDDDJcsIrY0KA99KPg+UmSjxjPz7+Eu9mO5GaSNn0vvVdsgrgjkh+35AS9k8Gn/DPaQJoNih+DpY5ZHsuY1zlvnvvk+hsCUHOATngARNs6yQMf2IrQqf38SlBPJ/xjt4oopLyqZuZ59xbFMFa0Yr/B7cCpxNpeIMCbwmc8YOtztOG1ZazlxB6eMTwp1V25TxFPh3PqUz9s37NmBEhkRiEyiJzlDSrKwz2y+77VWztQByM30lYAEXc5GwJD1LTaQwlv/thjhwveAzKLIpxzC5TbUjii7L+4iJF/JrjtXAEYmkegXj6lGBpRIdwXTYWMm3jG6gG+MV2nfWmocDzg3Q==",
11
+ :master_key_location => "/home/hudson/.ssh/id_rsa"
12
+ },
13
+ :gem => {
14
+ :install => "hudson --pre",
15
+ :version => "hudson-0.3.0.beta.13"
16
+ }
17
+ })
@@ -0,0 +1,3 @@
1
+ recipes('main')
2
+ owner_name(@attribute[:users].first[:username])
3
+ owner_pass(@attribute[:users].first[:password])
@@ -0,0 +1,6 @@
1
+ define :ey_cloud_report do
2
+ execute "reporting for #{params[:name]}" do
3
+ command "ey-enzyme --report '#{params[:message]}'"
4
+ epic_fail true
5
+ end
6
+ end
@@ -0,0 +1,40 @@
1
+
2
+ class Chef
3
+ class Resource
4
+ class RubyBlock < Chef::Resource
5
+ def initialize(name, collection=nil, node=nil)
6
+ super(name, collection, node)
7
+ @resource_name = :ruby_block
8
+ @action = :create
9
+ @allowed_actions.push(:create)
10
+ end
11
+
12
+ def block(&block)
13
+ if block
14
+ @block = block
15
+ else
16
+ @block
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+
24
+ class Chef
25
+ class Provider
26
+ class RubyBlock < Chef::Provider
27
+ def load_current_resource
28
+ Chef::Log.debug(@new_resource.inspect)
29
+ true
30
+ end
31
+
32
+ def action_create
33
+ @new_resource.block.call
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ Chef::Platform.platforms[:default].merge! :ruby_block => Chef::Provider::RubyBlock
40
+
@@ -0,0 +1,12 @@
1
+ class Chef
2
+ class Recipe
3
+ def run_for_app(*apps, &block)
4
+ apps.map! {|a| a.to_s }
5
+ node[:applications].map{|k,v| [k,v] }.sort_by {|a,b| a }.each do |name, app_data|
6
+ if apps.include?(name)
7
+ block.call(name, app_data)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,83 @@
1
+ #
2
+ # Cookbook Name:: hudson_slave
3
+ # Recipe:: default
4
+ #
5
+
6
+ env_name = node[:environment][:name]
7
+ username = node[:users].first[:username]
8
+
9
+ if ['solo','app_master'].include?(node[:instance_role]) && env_name =~ /_(ci|hudson_slave)$/
10
+ # gem_package "hudson" do
11
+ # source "http://gemcutter.org"
12
+ # version "0.3.0.beta.3"
13
+ # action :install
14
+ # end
15
+
16
+ execute "install_hudson_in_resin" do
17
+ command "/usr/local/ey_resin/ruby/bin/gem install #{node[:hudson_slave][:gem][:install]}"
18
+ not_if { FileTest.directory?("/usr/local/ey_resin/ruby/gems/1.8/gems/#{node[:hudson_slave][:gem][:version]}") }
19
+ end
20
+
21
+ ruby_block "authorize_hudson_master_key" do
22
+ authorized_keys = "/home/#{node[:users].first[:username]}/.ssh/authorized_keys"
23
+ block do
24
+ File.open(authorized_keys, "a") do |f|
25
+ f.puts node[:hudson_slave][:master][:public_key]
26
+ end
27
+ end
28
+ not_if "grep '#{node[:hudson_slave][:master][:public_key]}' #{authorized_keys}"
29
+ end
30
+
31
+ execute "setup-git-config-for-tagging" do
32
+ command %Q{ sudo su #{username} -c "git config --global user.email 'you@example.com' && git config --global user.name 'You are Special'" }
33
+ not_if %Q{ sudo su #{username} -c "git config user.email" }
34
+ end
35
+
36
+ ruby_block "add-slave-to-master" do
37
+ block do
38
+ Gem.clear_paths
39
+ require "hudson"
40
+ require "hudson/config"
41
+
42
+ Hudson::Api.setup_base_url(node[:hudson_slave][:master])
43
+
44
+ # Tell master about this slave
45
+ Hudson::Api.add_node(
46
+ :name => env_name,
47
+ :description => "Automatically added by Engine Yard AppCloud for environment #{env_name}",
48
+ :slave_host => node[:engineyard][:environment][:instances].first[:public_hostname],
49
+ :slave_user => username,
50
+ :executors => [node[:applications].size, 1].max,
51
+ :label => node[:applications].keys.join(" ")
52
+ )
53
+ end
54
+ action :create
55
+ end
56
+
57
+ ruby_block "tell-master-about-new-jobs" do
58
+ block do
59
+ begin
60
+ job_names = Hudson::Api.summary["jobs"].map {|job| job["name"]} # TODO Hudson::Api.job_names
61
+ app_names = node[:applications].keys
62
+ apps_to_add = app_names - job_names
63
+
64
+ # Tell server about each application
65
+ apps_to_add.each do |app_name|
66
+ data = node[:applications][app_name]
67
+
68
+ job_config = Hudson::JobConfigBuilder.new("rails") do |c|
69
+ c.scm = data[:repository_name]
70
+ c.assigned_node = app_name
71
+ end
72
+
73
+ Hudson::Api.create_job(app_name, job_config)
74
+ Hudson::Api.build_job(app_name)
75
+ end
76
+ rescue Errno::ECONNREFUSED, Errno::EAFNOSUPPORT
77
+ raise Exception, "No connection available to the Hudson server (#{Hudson::Api.base_uri})."
78
+ end
79
+ end
80
+ action :create
81
+ end
82
+
83
+ end
metadata ADDED
@@ -0,0 +1,213 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: engineyard-hudson
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Dr Nic Williams
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-30 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: thor
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 33
30
+ segments:
31
+ - 0
32
+ - 14
33
+ - 3
34
+ version: 0.14.3
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: engineyard
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 29
46
+ segments:
47
+ - 1
48
+ - 3
49
+ - 3
50
+ version: 1.3.3
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: rake
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 49
62
+ segments:
63
+ - 0
64
+ - 8
65
+ - 7
66
+ version: 0.8.7
67
+ type: :development
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: cucumber
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ hash: 61
78
+ segments:
79
+ - 0
80
+ - 9
81
+ - 3
82
+ version: 0.9.3
83
+ type: :development
84
+ version_requirements: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ name: rspec
87
+ prerelease: false
88
+ requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ hash: 13
94
+ segments:
95
+ - 2
96
+ - 0
97
+ - 1
98
+ version: 2.0.1
99
+ type: :development
100
+ version_requirements: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ name: json
103
+ prerelease: false
104
+ requirement: &id006 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ hash: 7
110
+ segments:
111
+ - 1
112
+ - 4
113
+ - 0
114
+ version: 1.4.0
115
+ type: :development
116
+ version_requirements: *id006
117
+ - !ruby/object:Gem::Dependency
118
+ name: awesome_print
119
+ prerelease: false
120
+ requirement: &id007 !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ hash: 3
126
+ segments:
127
+ - 0
128
+ version: "0"
129
+ type: :development
130
+ version_requirements: *id007
131
+ description: Run your continuous integration (CI) tests against your Engine Yard AppCloud environments - the exact same configuration you are using in production!
132
+ email:
133
+ - drnicwilliams@gmail.com
134
+ executables:
135
+ - ey-hudson
136
+ extensions: []
137
+
138
+ extra_rdoc_files: []
139
+
140
+ files:
141
+ - .gitignore
142
+ - Gemfile
143
+ - Gemfile.lock
144
+ - History.md
145
+ - README.md
146
+ - Rakefile
147
+ - bin/ey-hudson
148
+ - engineyard-hudson.gemspec
149
+ - features/install.feature
150
+ - features/server.feature
151
+ - features/step_definitions/common_steps.rb
152
+ - features/step_definitions/fixture_project_steps.rb
153
+ - features/step_definitions/hudson_steps.rb
154
+ - features/support/common.rb
155
+ - features/support/env.rb
156
+ - features/support/matchers.rb
157
+ - fixtures/cookbooks/main/recipes/default.rb
158
+ - fixtures/cookbooks/redis/recipes/default.rb
159
+ - fixtures/projects/rails/Gemfile
160
+ - fixtures/projects/rails/Gemfile.lock
161
+ - fixtures/projects/rails/Rakefile
162
+ - lib/engineyard-hudson.rb
163
+ - lib/engineyard-hudson/cli.rb
164
+ - lib/engineyard-hudson/cli/install.rb
165
+ - lib/engineyard-hudson/cli/install/templates/attributes.rb.erb
166
+ - lib/engineyard-hudson/cli/install/templates/cookbooks/main/attributes/recipe.rb
167
+ - lib/engineyard-hudson/cli/install/templates/cookbooks/main/definitions/ey_cloud_report.rb
168
+ - lib/engineyard-hudson/cli/install/templates/cookbooks/main/libraries/ruby_block.rb
169
+ - lib/engineyard-hudson/cli/install/templates/cookbooks/main/libraries/run_for_app.rb
170
+ - lib/engineyard-hudson/cli/install/templates/recipes.rb
171
+ has_rdoc: true
172
+ homepage: http://github.com/engineyard/engineyard-hudson
173
+ licenses: []
174
+
175
+ post_install_message:
176
+ rdoc_options: []
177
+
178
+ require_paths:
179
+ - lib
180
+ required_ruby_version: !ruby/object:Gem::Requirement
181
+ none: false
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ hash: 3
186
+ segments:
187
+ - 0
188
+ version: "0"
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ none: false
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ hash: 3
195
+ segments:
196
+ - 0
197
+ version: "0"
198
+ requirements: []
199
+
200
+ rubyforge_project: engineyard-hudson
201
+ rubygems_version: 1.3.7
202
+ signing_key:
203
+ specification_version: 3
204
+ summary: Easier to do CI than not to. Use Hudson CI on Engine Yard AppCloud.
205
+ test_files:
206
+ - features/install.feature
207
+ - features/server.feature
208
+ - features/step_definitions/common_steps.rb
209
+ - features/step_definitions/fixture_project_steps.rb
210
+ - features/step_definitions/hudson_steps.rb
211
+ - features/support/common.rb
212
+ - features/support/env.rb
213
+ - features/support/matchers.rb