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.
@@ -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