cany 0.0.2 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -0
- data/Gemfile +12 -0
- data/Guardfile +20 -0
- data/Rakefile +11 -0
- data/lib/cany.rb +88 -19
- data/lib/cany/dependency.rb +70 -0
- data/lib/cany/dpkg/builder.rb +4 -16
- data/lib/cany/dpkg/creator.rb +39 -29
- data/lib/cany/dpkg/deb_helper_recipe.rb +105 -20
- data/lib/cany/errors.rb +62 -0
- data/lib/cany/mixins/depend_mixin.rb +22 -0
- data/lib/cany/recipe.rb +182 -11
- data/lib/cany/recipes/bundler.rb +28 -5
- data/lib/cany/recipes/bundler/gem.rb +50 -0
- data/lib/cany/recipes/bundler/gem_db.rb +27 -0
- data/lib/cany/recipes/rails.rb +19 -2
- data/lib/cany/recipes/sidekiq.rb +38 -0
- data/lib/cany/recipes/thin.rb +24 -18
- data/lib/cany/recipes/unicorn.rb +16 -0
- data/lib/cany/recipes/web_server.rb +5 -19
- data/lib/cany/specification.rb +49 -1
- data/lib/cany/specification/dsl.rb +23 -3
- data/lib/cany/version.rb +2 -2
- data/spec/cany/dependency_spec.rb +120 -0
- data/spec/cany/dpkg/builder_spec.rb +38 -0
- data/spec/{dpkg → cany/dpkg}/creator_spec.rb +26 -2
- data/spec/cany/dpkg/deb_helper_recipe_spec.rb +91 -0
- data/spec/cany/recipe_spec.rb +213 -0
- data/spec/cany/recipes/bundler/gem_spec.rb +46 -0
- data/spec/cany/recipes/bundler_spec.rb +29 -0
- data/spec/cany/recipes/rails_spec.rb +28 -0
- data/spec/cany/recipes/sidekiq_spec.rb +52 -0
- data/spec/cany/specification_spec.rb +113 -1
- data/spec/spec_helper.rb +31 -4
- metadata +29 -6
- data/spec/dpkg/builder_spec.rb +0 -53
data/lib/cany/recipes/rails.rb
CHANGED
@@ -2,20 +2,37 @@ module Cany
|
|
2
2
|
module Recipes
|
3
3
|
class Rails < Recipe
|
4
4
|
register_as :rails
|
5
|
+
hook :env
|
6
|
+
|
7
|
+
class DSL < Recipe::DSL
|
8
|
+
delegate :compile_assets
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :compile_assets
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
@compile_assets = true
|
15
|
+
super
|
16
|
+
end
|
5
17
|
|
6
18
|
def clean
|
7
19
|
rmtree 'tmp', 'public/assets'
|
8
20
|
inner.clean
|
9
21
|
end
|
10
22
|
|
23
|
+
def prepare
|
24
|
+
recipe(:bundler).configure :env_vars, RAILS_ENV: 'production'
|
25
|
+
end
|
26
|
+
|
11
27
|
def build
|
12
|
-
|
13
|
-
ruby_bin 'bundle', 'exec', 'rake', 'assets:precompile'
|
28
|
+
run_hook :env, :before
|
14
29
|
ENV['RAILS_ENV'] = 'production'
|
30
|
+
ruby_bin 'bundle', 'exec', 'rake', 'assets:precompile' if compile_assets
|
15
31
|
inner.build
|
16
32
|
end
|
17
33
|
|
18
34
|
def binary
|
35
|
+
run_hook :env, :after
|
19
36
|
%w(app config.ru db Gemfile Gemfile.lock lib public Rakefile vendor).each do |item|
|
20
37
|
install item, "/usr/share/#{spec.name}" if File.exists? item
|
21
38
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Cany::Recipes
|
2
|
+
# This recipes installs Sidekiq
|
3
|
+
# "Simple, efficient, background processing in Ruby"
|
4
|
+
# Sidekiq is registered as service and so automatically started.
|
5
|
+
# @see http://sidekiq.org/ The official website for more information.
|
6
|
+
# @node The recipe relies that 'sidekiq' is listed inside your Gemfile and
|
7
|
+
# therefore installed via the 'bundler' recipe.
|
8
|
+
class Sidekiq < Cany::Recipe
|
9
|
+
register_as :sidekiq
|
10
|
+
|
11
|
+
attr_accessor :queues
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
@queues = []
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
class DSL < Cany::Recipe::DSL
|
19
|
+
def queue(name)
|
20
|
+
@recipe.queues << name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def binary
|
25
|
+
install_service name, %W(/usr/bin/#{spec.name} sidekiq) + sidekiq_args, user: 'www-data', group: 'www-data'
|
26
|
+
inner.binary
|
27
|
+
end
|
28
|
+
|
29
|
+
def sidekiq_args
|
30
|
+
args = %w(--environment production)
|
31
|
+
if queues.any?
|
32
|
+
args << '--queue'
|
33
|
+
args << queues.join(',')
|
34
|
+
end
|
35
|
+
args
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/cany/recipes/thin.rb
CHANGED
@@ -1,25 +1,31 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
|
3
|
-
module Cany
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
module Cany::Recipes
|
4
|
+
# This recipes install the thin ruby web server. It is registered
|
5
|
+
# and started as service.
|
6
|
+
#
|
7
|
+
# A simple thin configuration file (/etc/<application name>/thin.yml) is
|
8
|
+
# created and can be adjusted to the user needs.
|
9
|
+
#
|
10
|
+
# @see http://code.macournoyer.com/thin/ The project website for more
|
11
|
+
# information about the project
|
12
|
+
# @note The receives relies that the 'thin' gem is included in your Gemfile
|
13
|
+
# and therefore installed via bundler (and the bundler recipe).
|
14
|
+
class Thin < WebServer
|
15
|
+
register_as :thin
|
9
16
|
|
10
|
-
|
11
|
-
|
12
|
-
|
17
|
+
def launch_command
|
18
|
+
%W(thin start -C /etc/#{spec.name}/thin.yml")
|
19
|
+
end
|
13
20
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
21
|
+
def binary
|
22
|
+
default_options = {
|
23
|
+
'environment' => 'production',
|
24
|
+
'socket' => "/var/run/#{spec.name}/sock",
|
25
|
+
'pid' => "/var/run/#{spec.name}/thin.pid"
|
26
|
+
}
|
27
|
+
install_content "/etc/#{spec.name}/thin.yml", default_options.to_yaml
|
28
|
+
super
|
23
29
|
end
|
24
30
|
end
|
25
31
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Cany::Recipes
|
2
|
+
# This recipes install the Unicorn Rack HTTP server. It is registered
|
3
|
+
# and started as service.
|
4
|
+
#
|
5
|
+
# @see http://unicorn.bogomips.org/ The project website for more
|
6
|
+
# information about the project
|
7
|
+
# @note The receives relies that the 'unicorn' gem is included in your
|
8
|
+
# Gemfile and therefore installed via bundler (and the bundler recipe).
|
9
|
+
class Unicorn < WebServer
|
10
|
+
register_as :unicorn
|
11
|
+
|
12
|
+
def launch_command
|
13
|
+
%W(unicorn start -c /etc/#{spec.name}/unicorn.rb -E production)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -2,25 +2,11 @@ module Cany
|
|
2
2
|
module Recipes
|
3
3
|
class WebServer < Recipe
|
4
4
|
def binary
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
f.write "respawn\n"
|
12
|
-
f.write "respawn limit 10 5\n"
|
13
|
-
f.write "umask 022\n"
|
14
|
-
|
15
|
-
f.write "chdir /usr/share/#{spec.name}\n"
|
16
|
-
|
17
|
-
f.write "pre-start script\n"
|
18
|
-
f.write "\tmkdir -p /var/run/#{spec.name}\n"
|
19
|
-
f.write "\tchown www-data /var/run/#{spec.name}\n"
|
20
|
-
f.write "end script\n"
|
21
|
-
|
22
|
-
f.write "exec su www-data --shell /usr/bin/#{spec.name} -- #{launch_command}\n"
|
23
|
-
end
|
5
|
+
recipe(:system).configure :service_pre_scripts, {
|
6
|
+
mkdir_run: "mkdir /var/run/#{spec.name}",
|
7
|
+
chown_run: "chown /var/run/#{spec.name}"
|
8
|
+
}
|
9
|
+
install_service name, ["/usr/bin/#{spec.name}"] + launch_command, user: 'www-data', group: 'www-data'
|
24
10
|
inner.binary
|
25
11
|
end
|
26
12
|
end
|
data/lib/cany/specification.rb
CHANGED
@@ -6,16 +6,64 @@ module Cany
|
|
6
6
|
EXT = 'canspec'
|
7
7
|
|
8
8
|
attr_accessor :name, :description, :maintainer_name, :maintainer_email, :website, :licence, :version
|
9
|
-
attr_accessor :base_dir, :recipes
|
9
|
+
attr_accessor :base_dir, :recipes, :dependencies, :cany_version_constraint
|
10
10
|
attr_accessor :build, :binary
|
11
11
|
|
12
12
|
def initialize(dsl=Cany::Specification::DSL, &block)
|
13
13
|
@recipes = []
|
14
|
+
@dependencies = []
|
14
15
|
setup dsl, &block
|
15
16
|
end
|
16
17
|
|
17
18
|
def setup(dsl=Cany::Specification::DSL, &block)
|
18
19
|
dsl.new(self).exec(&block)
|
19
20
|
end
|
21
|
+
|
22
|
+
# @api public
|
23
|
+
# Return all dependencies needed to build this package
|
24
|
+
# @return [Array<Dependency>] A list of dependencies.
|
25
|
+
def build_dependencies
|
26
|
+
dependencies.select(&:build?)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @api public
|
30
|
+
# Return all dependencies needed to build this package
|
31
|
+
# @return [Array<Dependency>] A list of dependencies.
|
32
|
+
def runtime_dependencies
|
33
|
+
dependencies.select(&:runtime?)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Define the given recipe instance as system recipe. It can be accessed by other recipes under
|
37
|
+
# the system name and it is added as last recipe and do system specific things.
|
38
|
+
# At the moment the only recipe designed to work as system recipe is the DebHelperRecipe
|
39
|
+
# The attribute should be set after all recipe changes are over.
|
40
|
+
# @param recipe[Recipe] the system recipe instance
|
41
|
+
def system_recipe=(recipe)
|
42
|
+
@system_recipe = recipe
|
43
|
+
@recipes << recipe
|
44
|
+
end
|
45
|
+
# Return the system recipe for this specification.
|
46
|
+
# @return [Recipe] The system reciep
|
47
|
+
# @raise [NoSystemRecipe] if no system recipe was specified.
|
48
|
+
def system_recipe
|
49
|
+
raise NoSystemRecipe.new unless @system_recipe
|
50
|
+
@system_recipe
|
51
|
+
end
|
52
|
+
|
53
|
+
# @api private
|
54
|
+
# This method creates recipe instances for all required recipes from the given spec.
|
55
|
+
def setup_recipes
|
56
|
+
last_recipe = nil
|
57
|
+
recipes.reverse.each do |recipe|
|
58
|
+
recipe.inner = last_recipe unless last_recipe.nil?
|
59
|
+
last_recipe = recipe
|
60
|
+
end
|
61
|
+
recipes.map(&:prepare)
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
"Cany::Specification<#{object_id}>(name=#{name}, recipes=[#{recipes.map(&:name).join(', ')}]"
|
66
|
+
end
|
67
|
+
alias_method :inspect, :to_s
|
20
68
|
end
|
21
69
|
end
|
@@ -6,7 +6,7 @@ module Cany
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def exec(&block)
|
9
|
-
instance_eval
|
9
|
+
instance_eval(&block)
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.delegate(*methods)
|
@@ -21,10 +21,21 @@ module Cany
|
|
21
21
|
|
22
22
|
delegate :name, :description, :maintainer_name, :maintainer_email, :website, :licence, :version
|
23
23
|
|
24
|
+
# This directive ensures that Cany is used in a specific version. It will pass if the version
|
25
|
+
# match or raise an exception if it is an unsupported version.
|
26
|
+
# @param version [String] The version constrain the must be satisfied by Cany.
|
27
|
+
# @raise Cany::UnsupportedVersion on version mismatch
|
28
|
+
def require_cany(version)
|
29
|
+
unless Gem::Requirement.create(version).satisfied_by? Gem::Version.new(Cany::VERSION)
|
30
|
+
raise UnsupportedVersion.new version
|
31
|
+
end
|
32
|
+
@specification.cany_version_constraint = version
|
33
|
+
end
|
34
|
+
|
24
35
|
# This include the given recipe into the build process.
|
25
36
|
# @param [Symbol] name The name of the recipe as symbol.
|
26
|
-
def use(name)
|
27
|
-
@specification.recipes << name
|
37
|
+
def use(name, &block)
|
38
|
+
@specification.recipes << Cany::Recipe.from_name(name).new(@specification, &block)
|
28
39
|
end
|
29
40
|
|
30
41
|
def build(&block)
|
@@ -34,6 +45,15 @@ module Cany
|
|
34
45
|
def binary(&block)
|
35
46
|
@specification.binary = block
|
36
47
|
end
|
48
|
+
|
49
|
+
# @api public
|
50
|
+
# Adds a new dependency for the software. See Cany::Dependency for a more
|
51
|
+
# abstract description about dependencies
|
52
|
+
# See Cany::Mixins::DependMixin for parameter description
|
53
|
+
include Mixins::DependMixin
|
54
|
+
def depend(*args)
|
55
|
+
@specification.dependencies << create_dep(*args)
|
56
|
+
end
|
37
57
|
end
|
38
58
|
end
|
39
59
|
end
|
data/lib/cany/version.rb
CHANGED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cany::Dependency do
|
4
|
+
let(:dep) { Cany::Dependency.new }
|
5
|
+
subject { dep.determine(distro, release) }
|
6
|
+
let(:distro) { :ubuntu }
|
7
|
+
let(:release) { :precise }
|
8
|
+
|
9
|
+
context 'without defined packages' do
|
10
|
+
it 'should return a empty list' do
|
11
|
+
expect(subject).to match_array []
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'without distro information' do
|
16
|
+
context 'and a simple default package' do
|
17
|
+
before { dep.define_default('example') }
|
18
|
+
it 'should return the packages and nil as version' do
|
19
|
+
expect(subject).to match_array [['example', nil]]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'and a default package with version' do
|
24
|
+
before { dep.define_default('example', '>= 1.7') }
|
25
|
+
it 'should return the packages and its version' do
|
26
|
+
expect(subject).to match_array [['example', '>= 1.7']]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with multiple default packages' do
|
31
|
+
before do
|
32
|
+
dep.define_default('example')
|
33
|
+
dep.define_default('otto')
|
34
|
+
end
|
35
|
+
it 'should return all default packages' do
|
36
|
+
expect(subject).to match_array [['otto', nil], ['example', nil]]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with distribution default packages' do
|
42
|
+
before { dep.define_on_distro(distro, 'hans') }
|
43
|
+
|
44
|
+
it 'should return distro packages' do
|
45
|
+
expect(subject).to match_array [['hans', nil]]
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'and with default packages' do
|
49
|
+
before { dep.define_default('default') }
|
50
|
+
it 'should only return distro package' do
|
51
|
+
expect(subject).to match_array [['hans', nil]]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'and a second distro package' do
|
56
|
+
before { dep.define_on_distro(distro, 'otto') }
|
57
|
+
it 'should return both packages' do
|
58
|
+
expect(subject).to match_array [['hans', nil], ['otto', nil]]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'with distribution release information' do
|
64
|
+
before { dep.define_on_distro_release(distro, release, 'distrorelease') }
|
65
|
+
it 'should return distro release packages' do
|
66
|
+
expect(subject).to match_array [['distrorelease', nil]]
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with additional distrorelease packages with version' do
|
70
|
+
before { dep.define_on_distro_release(distro, release, 'add', '!= 2.8') }
|
71
|
+
it 'should return both packages with its version or nil' do
|
72
|
+
expect(subject).to match_array [['distrorelease', nil], ['add', '!= 2.8']]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with distro packages' do
|
77
|
+
before { dep.define_on_distro(distro, 'other') }
|
78
|
+
it 'should return only distrorelease packages' do
|
79
|
+
expect(subject).to match_array [['distrorelease', nil]]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'situations property' do
|
85
|
+
subject { dep.situations }
|
86
|
+
it 'should be runtime per default' do
|
87
|
+
should match_array [:runtime]
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'set to single value' do
|
91
|
+
before { dep.situations = :build }
|
92
|
+
it 'should convert them to an array' do
|
93
|
+
should match_array [:build]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'set to multiple values' do
|
98
|
+
before { dep.situations = [:build, :runtime] }
|
99
|
+
it 'should remain the array' do
|
100
|
+
should match_array [:build, :runtime]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'accessabled via shortcut properties' do
|
105
|
+
subject { dep }
|
106
|
+
it 'should be runtime and not build per default' do
|
107
|
+
should be_runtime
|
108
|
+
should_not be_build
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'with changed value' do
|
112
|
+
before { dep.situations = [:build, :hans] }
|
113
|
+
it 'should behave accordingly' do
|
114
|
+
should_not be_runtime
|
115
|
+
should be_build
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cany::Dpkg::Builder do
|
4
|
+
let!(:dir) { Dir.mktmpdir }
|
5
|
+
after { FileUtils.remove_entry dir}
|
6
|
+
let(:spec) do
|
7
|
+
s = Cany::Specification.new do
|
8
|
+
name 'dpkg-creator-test'
|
9
|
+
version '0.1'
|
10
|
+
description 'Test Project'
|
11
|
+
maintainer_name 'Hans Otto'
|
12
|
+
maintainer_email 'hans.otto@example.org'
|
13
|
+
website 'http://example.org'
|
14
|
+
licence 'GPL-2+'
|
15
|
+
end
|
16
|
+
s.base_dir = dir
|
17
|
+
s
|
18
|
+
end
|
19
|
+
let(:builder) { Cany::Dpkg::Builder.new(spec) }
|
20
|
+
|
21
|
+
describe '#run' do
|
22
|
+
subject { builder.run 'clean' }
|
23
|
+
it 'should set system_recipe' do
|
24
|
+
expect(spec).to receive(:system_recipe=).with(kind_of(Cany::Dpkg::DebHelperRecipe)).and_call_original
|
25
|
+
subject
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should setup recipes' do
|
29
|
+
expect(spec).to receive(:setup_recipes).and_call_original
|
30
|
+
subject
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should call delegate the clean action to the loaded recipes' do
|
34
|
+
expect_any_instance_of(Cany::Dpkg::DebHelperRecipe).to receive(:clean)
|
35
|
+
subject
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|