cany 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- ENV['RAILS_ENV'] = 'assets'
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
@@ -1,25 +1,31 @@
1
1
  require 'yaml'
2
2
 
3
- module Cany
4
- module Recipes
5
- # This recipes runs the application with the thin web server. At the moment it installs only a
6
- # upstart start script. Other init scripts are planed (e.g. systemd for debian).
7
- class Thin < WebServer
8
- register_as :thin
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
- def launch_command
11
- "thin start -C /etc/#{spec.name}/thin.yml"
12
- end
17
+ def launch_command
18
+ %W(thin start -C /etc/#{spec.name}/thin.yml")
19
+ end
13
20
 
14
- def binary
15
- default_options = {
16
- 'environment' => 'production',
17
- 'socket' => "/var/run/#{spec.name}/sock",
18
- 'pid' => "/var/run/#{spec.name}/thin.pid"
19
- }
20
- install_content "/etc/#{spec.name}/thin.yml", default_options.to_yaml
21
- super
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
- File.open File.join('debian', "#{spec.name}.upstart"), 'w' do |f|
6
- f.write "description \"#{spec.description}\"\n"
7
-
8
- f.write "start on filesystem or runlevel [2345]\n"
9
- f.write "stop on runlevel [!2345]\n"
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
@@ -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 &block
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
@@ -1,8 +1,8 @@
1
1
  module Cany
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 0
5
- PATCH = 2
4
+ MINOR = 1
5
+ PATCH = 0
6
6
  STAGE = nil
7
7
  STRING = [MAJOR, MINOR, PATCH, STAGE].reject(&:nil?).join('.')
8
8
  def self.to_s; STRING end
@@ -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