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.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +47 -0
- data/History.md +6 -0
- data/README.md +74 -0
- data/Rakefile +19 -0
- data/bin/ey-hudson +7 -0
- data/engineyard-hudson.gemspec +29 -0
- data/features/install.feature +57 -0
- data/features/server.feature +12 -0
- data/features/step_definitions/common_steps.rb +191 -0
- data/features/step_definitions/fixture_project_steps.rb +14 -0
- data/features/step_definitions/hudson_steps.rb +3 -0
- data/features/support/common.rb +37 -0
- data/features/support/env.rb +13 -0
- data/features/support/matchers.rb +10 -0
- data/fixtures/cookbooks/main/recipes/default.rb +1 -0
- data/fixtures/cookbooks/redis/recipes/default.rb +0 -0
- data/fixtures/projects/rails/Gemfile +3 -0
- data/fixtures/projects/rails/Gemfile.lock +10 -0
- data/fixtures/projects/rails/Rakefile +4 -0
- data/lib/engineyard-hudson.rb +4 -0
- data/lib/engineyard-hudson/cli.rb +44 -0
- data/lib/engineyard-hudson/cli/install.rb +62 -0
- data/lib/engineyard-hudson/cli/install/templates/attributes.rb.erb +17 -0
- data/lib/engineyard-hudson/cli/install/templates/cookbooks/main/attributes/recipe.rb +3 -0
- data/lib/engineyard-hudson/cli/install/templates/cookbooks/main/definitions/ey_cloud_report.rb +6 -0
- data/lib/engineyard-hudson/cli/install/templates/cookbooks/main/libraries/ruby_block.rb +40 -0
- data/lib/engineyard-hudson/cli/install/templates/cookbooks/main/libraries/run_for_app.rb +12 -0
- data/lib/engineyard-hudson/cli/install/templates/recipes.rb +83 -0
- metadata +213 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -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)
|
data/History.md
ADDED
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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"]
|
data/bin/ey-hudson
ADDED
@@ -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,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 @@
|
|
1
|
+
require_recipe 'redis'
|
File without changes
|
@@ -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,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,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
|