cany 0.0.1

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,28 @@
1
+ module Cany
2
+ module Dpkg
3
+ class DebHelperRecipe < Cany::Recipe
4
+ register_as :deb_helper
5
+
6
+ def initialize(*args)
7
+ super *args
8
+ @log = File.read('debian/xikolo-account.debhelper.log') if File.exists? 'debian/xikolo-account.debhelper.log'
9
+ exec 'dh_prep'
10
+ end
11
+
12
+ def clean
13
+ exec %w(dh clean)
14
+ end
15
+
16
+ def build
17
+ instance_eval &spec.build if spec.build
18
+ exec %w(dh build)
19
+ end
20
+
21
+ def binary
22
+ instance_eval &spec.binary if spec.binary
23
+ File.write('debian/xikolo-account.debhelper.log', @log)
24
+ exec %w(dh binary)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,128 @@
1
+ require 'fileutils'
2
+
3
+ module Cany
4
+ class Recipe
5
+
6
+ # @api public
7
+ # This method should be call in subclasses to register new recipe instances. Cany ignores any
8
+ # recipe subclasses which does not call register_as. If multiple recipes register on the same
9
+ # name the later one will overwrite the earlier one and therefore used by Cany.
10
+ # @param [Symbol] name A ruby symbol as name for the recipe. It should be short and recognizable
11
+ def self.register_as(name)
12
+ @@recipes ||= {}
13
+ @@recipes[name] = self
14
+ end
15
+
16
+ # @api public
17
+ # Looks for the class registered for the given name
18
+ # @param [Symbol] name the name the class is search for
19
+ # @return [Cany::Recipe, nil] Returns the found class or nil if no class is registered on this
20
+ # name
21
+ def self.from_name(name)
22
+ @@recipes[name]
23
+ end
24
+
25
+ # Creates a new instance of this recipe
26
+ # @param [Cany::Specification] spec Specification object
27
+ # @param [Cany::Recipe, nil] inner Inner recipes should should be call between the pre and post
28
+ # actions of this class. Nil means most inner recipes.
29
+ def initialize(spec, inner)
30
+ @spec = spec
31
+ @inner = inner
32
+ end
33
+
34
+ attr_reader :spec, :inner
35
+
36
+ # API to use inside the recipe
37
+ ##############################
38
+
39
+ # @api public
40
+ # Run a command inside the build directory. In most cases it is not needed to call this method
41
+ # directly. Look at the other helper methods.
42
+ #
43
+ # The method expects as arguments the program name and additional parameters for the program.
44
+ # The arguments can be group with arguments, but must not:
45
+ # @example
46
+ # exec 'echo', %w(a b)
47
+ # exec ['echo', 'a', 'b']
48
+ # exec 'echo', 'a', 'b'
49
+ def exec(*args)
50
+ puts " #{args.flatten.join(' ')}"
51
+ system *args.flatten
52
+ end
53
+
54
+ # @api public
55
+ # Run a ruby task (like gem, bundler, rake ...)
56
+ #
57
+ # The method expects as arguments the program name and additional parameters for the program.
58
+ # See exec for more examples
59
+ def ruby_bin(*args)
60
+ exec 'ruby', '-S', *args
61
+ end
62
+
63
+ # @api public
64
+ # Install files or directory from the build directory
65
+ # @param [String] source The relative file name to a filename or directory inside the build
66
+ # directory that should be installed/copied into the destination package
67
+ # @param [String] destination The diretory name into that the file or directory should be
68
+ # installed
69
+ def install(src, dest_dir)
70
+ exec 'dh_install', src, dest_dir
71
+ end
72
+
73
+ # @api public
74
+ # Install a file. The content is passed as argument. This method is designed to be used by
75
+ # recipes to create files dynamically.
76
+ # @param [String] filename The absolute file name for the file inside the package.
77
+ # @param [String] content The file content
78
+ def install_content(filename, content)
79
+ FileUtils.mkdir_p File.dirname File.join('debian', spec.name, filename)
80
+ File.open File.join('debian', spec.name, filename), 'w' do |f|
81
+ f.write content
82
+ end
83
+ end
84
+
85
+ # @api public
86
+ # Installs/creates an empty directory
87
+ # @param [String] path The path name
88
+ def install_dir(path)
89
+ exec 'dh_installdirs', path
90
+ end
91
+
92
+ # @api public
93
+ # Create a file named destination as a link to a file named source
94
+ def install_link(source, destination)
95
+ exec 'dh_link', source, destination
96
+ end
97
+
98
+ # @api public
99
+ # Ensure that the given files or directories are no present. Directories are removed
100
+ # recursively.
101
+ def rmtree(*args)
102
+ args.flatten.each do |path|
103
+ ::FileUtils.remove_entry path if File.exists? path
104
+ end
105
+ end
106
+
107
+ # default implementation:
108
+ #########################
109
+
110
+ # @api public
111
+ # clean the build directory from all temporary and created files
112
+ def clean
113
+ inner.clean
114
+ end
115
+
116
+ # @api public
117
+ # build the program (means ./configure and make)
118
+ def build
119
+ inner.build
120
+ end
121
+
122
+ # @api public
123
+ # create binary (package) version of this file (means make install)
124
+ def binary
125
+ inner.binary
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,33 @@
1
+ module Cany
2
+ module Recipes
3
+ class Bundler < Cany::Recipe
4
+ register_as :bundler
5
+
6
+ def clean
7
+ rmtree 'bundler'
8
+ # rmtree 'vendor/bundle' -- do not remove gems, increase testing time
9
+ inner.clean
10
+ end
11
+
12
+ def build
13
+ ENV['GEM_PATH'] = 'bundler'
14
+ ENV['PATH'] = 'bundler/bin:' + ENV['PATH']
15
+ ruby_bin 'gem', %w(install bundler --no-ri --no-rdoc --install-dir bundler --bindir bundler/bin)
16
+ ruby_bin 'bundle', %w(install --deployment --without development test)
17
+ inner.build
18
+ end
19
+
20
+ def binary
21
+ install 'bundler', "/usr/share/#{spec.name}"
22
+ install '.bundle', "/usr/share/#{spec.name}"
23
+ install 'vendor/bundle', "/usr/share/#{spec.name}/vendor"
24
+ install_content "/usr/bin/#{spec.name}", "#!/bin/sh
25
+ cd /usr/share/#{spec.name}
26
+ export GEM_PATH=/usr/share/#{spec.name}/bundler
27
+ exec /usr/share/#{spec.name}/bundler/bin/bundle exec \"$@\"
28
+ "
29
+ inner.binary
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ module Cany
2
+ module Recipes
3
+ class Rails < Recipe
4
+ register_as :rails
5
+
6
+ def clean
7
+ rmtree 'tmp', 'public/assets'
8
+ inner.clean
9
+ end
10
+
11
+ def build
12
+ ruby_bin 'rake', 'assets:precompile'
13
+ inner.build
14
+ end
15
+
16
+ def binary
17
+ %w(app config.ru db Gemfile Gemfile.lock lib public Rakefile vendor).each do |item|
18
+ install item, "/usr/share/#{spec.name}" if File.exists? item
19
+ end
20
+
21
+ Dir.foreach('config') do |entry|
22
+ next if %w(. ..).include? entry
23
+ install File.join('config', entry), "/etc/#{spec.name}"
24
+ end
25
+ install_link "/etc/#{spec.name}", "/usr/share/#{spec.name}/config"
26
+
27
+ install_dir "/etc/#{spec.name}"
28
+ install_dir "/var/tmp/#{spec.name}"
29
+ install_dir "/var/log/#{spec.name}"
30
+
31
+ install_link "/var/log/#{spec.name}", "/usr/share/#{spec.name}/log"
32
+ install_link "/var/tmp/#{spec.name}", "/usr/share/#{spec.name}/tmp"
33
+ inner.binary
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ require 'yaml'
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
9
+
10
+ def launch_command
11
+ "thin start -C /etc/#{spec.name}/thin.yml"
12
+ end
13
+
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
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ module Cany
2
+ module Recipes
3
+ class WebServer < Recipe
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
24
+ inner.binary
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ module Cany
2
+
3
+ require 'cany/specification/dsl'
4
+
5
+ class Specification
6
+ EXT = 'canspec'
7
+
8
+ attr_accessor :name, :description, :maintainer_name, :maintainer_email, :website, :licence, :version
9
+ attr_accessor :base_dir, :recipes
10
+ attr_accessor :build, :binary
11
+
12
+ def initialize(dsl=Cany::Specification::DSL, &block)
13
+ @recipes = []
14
+ setup dsl, &block
15
+ end
16
+
17
+ def setup(dsl=Cany::Specification::DSL, &block)
18
+ dsl.new(self).exec(&block)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ module Cany
2
+ class Specification
3
+ class DSL
4
+ def initialize(specification)
5
+ @specification = specification
6
+ end
7
+
8
+ def exec(&block)
9
+ instance_eval &block
10
+ end
11
+
12
+ def self.delegate(*methods)
13
+ methods.each do |method|
14
+ module_eval(<<-EOS, __FILE__, __LINE__)
15
+ def #{method}(*args, &block)
16
+ @specification.send :'#{method}=', *args, &block
17
+ end
18
+ EOS
19
+ end
20
+ end
21
+
22
+ delegate :name, :description, :maintainer_name, :maintainer_email, :website, :licence, :version
23
+
24
+ # This include the given recipe into the build process.
25
+ # @param [Symbol] name The name of the recipe as symbol.
26
+ def use(name)
27
+ @specification.recipes << name
28
+ end
29
+
30
+ def build(&block)
31
+ @specification.build = block
32
+ end
33
+
34
+ def binary(&block)
35
+ @specification.binary = block
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ module Cany
2
+ module VERSION
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ PATCH = 1
6
+ STAGE = nil
7
+ STRING = [MAJOR, MINOR, PATCH, STAGE].reject(&:nil?).join('.')
8
+ def self.to_s; STRING end
9
+ end
10
+ end
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cany::Specification do
4
+ describe '#new' do
5
+ it 'should have a name' do
6
+ spec = Cany::Specification.new do
7
+ name 'example'
8
+ end
9
+ expect(spec.name).to eq 'example'
10
+ end
11
+
12
+ it 'should have a description' do
13
+ spec = Cany::Specification.new do
14
+ description 'Test hans otto project'
15
+ end
16
+ expect(spec.description).to eq 'Test hans otto project'
17
+ end
18
+
19
+ it 'should have a maintainer name' do
20
+ spec = Cany::Specification.new do
21
+ maintainer_name 'Otto Hans'
22
+ end
23
+ expect(spec.maintainer_name).to eq 'Otto Hans'
24
+ end
25
+
26
+ it 'should have a maintainer email' do
27
+ spec = Cany::Specification.new do
28
+ maintainer_email 'hans.otto@example.org'
29
+ end
30
+ expect(spec.maintainer_email).to eq 'hans.otto@example.org'
31
+ end
32
+
33
+ it 'should have a website' do
34
+ spec = Cany::Specification.new do
35
+ website 'http://example.org/~hans.otto/'
36
+ end
37
+ expect(spec.website).to eq 'http://example.org/~hans.otto/'
38
+ end
39
+
40
+ it 'should have a licence' do
41
+ spec = Cany::Specification.new do
42
+ licence 'MIT'
43
+ end
44
+ expect(spec.licence).to eq 'MIT'
45
+ end
46
+
47
+ it 'should have a version' do
48
+ spec = Cany::Specification.new do
49
+ version '0.1'
50
+ end
51
+ expect(spec.version).to eq '0.1'
52
+ end
53
+
54
+ it 'should be able to include recipes' do
55
+ spec = Cany::Specification.new do
56
+ use :bundler
57
+ end
58
+ expect(spec.recipes).to eq [:bundler]
59
+ end
60
+
61
+ it 'should accept a block for own build steps' do
62
+ spec = Cany::Specification.new do
63
+ build do
64
+ install 'hans', 'otto'
65
+ end
66
+ end
67
+ expect(spec.build).to be_a_kind_of Proc
68
+ end
69
+
70
+ it 'should accept a block for own build steps' do
71
+ spec = Cany::Specification.new do
72
+ binary do
73
+ install 'hans', 'otto'
74
+ end
75
+ end
76
+ expect(spec.binary).to be_a_kind_of Proc
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cany do
4
+ context '#setup' do
5
+ it 'should require a .canspec file' do
6
+ expect { Cany::setup File.expand_path('../fixtures/empty', __FILE__) }.to raise_error(Cany::MissingSpecification)
7
+ end
8
+ end
9
+
10
+ it 'should stop on multiple .canspec files' do
11
+ expect { Cany::setup File.expand_path('../fixtures/multiple', __FILE__) }.to raise_error Cany::MultipleSpecifications, /Multiple canspec found in .*/
12
+ end
13
+
14
+ it 'should load a .canspec file' do
15
+ spec = Cany::setup File.expand_path('../fixtures/single', __FILE__)
16
+ expect(spec).to be_an_instance_of Cany::Specification
17
+ expect(spec.name).to eq 'single-test'
18
+ expect(spec.base_dir).to eq File.expand_path('../fixtures/single', __FILE__)
19
+ end
20
+ end