cany 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +115 -0
- data/Rakefile +5 -0
- data/bin/cany +19 -0
- data/cany.gemspec +24 -0
- data/lib/cany.rb +29 -0
- data/lib/cany/dpkg.rb +4 -0
- data/lib/cany/dpkg/builder.rb +37 -0
- data/lib/cany/dpkg/creator.rb +145 -0
- data/lib/cany/dpkg/deb_helper_recipe.rb +28 -0
- data/lib/cany/recipe.rb +128 -0
- data/lib/cany/recipes/bundler.rb +33 -0
- data/lib/cany/recipes/rails.rb +37 -0
- data/lib/cany/recipes/thin.rb +25 -0
- data/lib/cany/recipes/web_server.rb +28 -0
- data/lib/cany/specification.rb +21 -0
- data/lib/cany/specification/dsl.rb +39 -0
- data/lib/cany/version.rb +10 -0
- data/spec/cany/specification_spec.rb +79 -0
- data/spec/cany_spec.rb +20 -0
- data/spec/dpkg/builder_spec.rb +53 -0
- data/spec/dpkg/creator_spec.rb +221 -0
- data/spec/fixtures/multiple/one.canspec +0 -0
- data/spec/fixtures/multiple/two.canspec +0 -0
- data/spec/fixtures/single/single.canspec +3 -0
- data/spec/fixtures/testgem.lock +10 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/file_matchers.rb +23 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 575282993ba4ffbbf4a1f932a57658c6d570965e
|
4
|
+
data.tar.gz: 8bcf88c7b8cf69da3108189061c06229f15b739e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 10d981813625901b4618e2d15679c0b898f064964f2780c8bbc6f2e2c1cadc9cbabdccfd5c138dd6d5c90183a5bfc8cca2d265465fc22c45c5f08a7c86aeffa9
|
7
|
+
data.tar.gz: da1ad37fd1fff55b7d7f0f9fa51840038bd44c5c0f4a57f0fb34bc0fc6b897b5a1414010857e0eddf01ac7d12230e88308a04dadea4e94764d25944e993cb309
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Malte Swart
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# Cany
|
2
|
+
|
3
|
+
Cany is a toolkit to create easily distribution packages from your (ruby) application. The goal is to reduce the packaging work to managing the application meta data (specification).
|
4
|
+
|
5
|
+
The goal is to support all (common) package manager - all from the some specification, although at the moment is only dpkg implemented.
|
6
|
+
|
7
|
+
The difference to other packaging tools is that cany uses as much tools as possible to build packages in the recommended way.
|
8
|
+
|
9
|
+
**Warning:** The gem has successfully built packages. But there are known limitations or problems. We collecting knowledge with the current usage and appreciate your feedback. With the 1.0 we will start with semantic visioning. Until then every updates tries to be as much compatible as possible but the more important goal is to create a better architecture and design -- so be careful when updating this gem.
|
10
|
+
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Cany is a rubygem, it needs **ruby >=1.9.3** to work. It is design to be lightweight, therefore a:
|
15
|
+
|
16
|
+
$ gem install cany
|
17
|
+
|
18
|
+
is enough to install and use it. But you can add is also to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'cany'
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
To let bundle install it for you.
|
29
|
+
|
30
|
+
|
31
|
+
## Write a Specification for your Application
|
32
|
+
|
33
|
+
Create a file ``<your application name>.canspec`` inside your application folder:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
Cany::Specification.new do
|
37
|
+
name 'example-application' # This is used as package name - please you only ASCII letters, - and literals.
|
38
|
+
version '0.1' # Your application's version
|
39
|
+
description 'This example application is used to have a application for that this README can describe how it is packaged with cany' # A short description for you application. Should not longer than a few paragraphs
|
40
|
+
maintainer_name 'Hans Otto' # The person/organisation maintaining the package
|
41
|
+
maintainer_email 'hans.otto@example.org' # A eMail address to contact the maintainer
|
42
|
+
website 'http://example.org/cany' # A website to get more information about the application
|
43
|
+
licence 'MIT' # Your Licence
|
44
|
+
|
45
|
+
# Use common build tasks to build your application - describing how your application needs to be build
|
46
|
+
# The ordering is important, see the recipes documentation for more information about the recipes itself and there recommended ordering
|
47
|
+
use :bundler
|
48
|
+
use :rails
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
|
53
|
+
## Recipes
|
54
|
+
|
55
|
+
Cany uses different recipes to share different packaging tasks above multiple applications.
|
56
|
+
|
57
|
+
### Bundler
|
58
|
+
|
59
|
+
All common applications need a bunch of gems. Bundle is the common tool to manage these gems. Because for most of the gems there exists no packages, we decide that the best approach is to ship the installed gems with the application.
|
60
|
+
|
61
|
+
This recipe is designed to use as first one. The installs bundler and uses the ``Gemfile.lock`` to install the referenced gems to ``vendor/bundle``.
|
62
|
+
|
63
|
+
### Rails
|
64
|
+
|
65
|
+
This recipe is used to install a ruby on rails application. The recipes installs the different files/directories.
|
66
|
+
|
67
|
+
### Thin
|
68
|
+
|
69
|
+
This recipe configures thin and install an init script to launch the application. It assumes that ``thin`` is listed in your Gemfile.
|
70
|
+
|
71
|
+
|
72
|
+
### Own Adjustments
|
73
|
+
|
74
|
+
There are applications were the known recipes are not sufficient to build it. You can define blocks inside the canspec that are runs like normal recipes and allow you to do specific tasks.
|
75
|
+
|
76
|
+
The following example installs an additional folder (of an Ruby on Rails applications):
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
Cany::Sepcification.new do
|
80
|
+
# meta data and recipe loading
|
81
|
+
binary do
|
82
|
+
install 'additional_subdir', "/usr/share/#{spec.name}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
Take a look at the ``Recipe`` class for helper you can use.
|
88
|
+
|
89
|
+
|
90
|
+
## Use Your Specification to Create Packages
|
91
|
+
|
92
|
+
### dpkg
|
93
|
+
|
94
|
+
To create dpkg packages (used by debian and its derivatives) run from your application directory with your canspec:
|
95
|
+
|
96
|
+
$ cany dpkg-create
|
97
|
+
|
98
|
+
It creates a debian subdirectory containing all needed files/information to build a dpkg package in the debian way. To create the package itself cany relies on the debian packaging tools. The build your package without signing the created files with GPG run
|
99
|
+
|
100
|
+
$ dpkg-buildpackage -us -uc
|
101
|
+
|
102
|
+
Afterwards you should have ``*.deb`` package in the directory containing your application directory.
|
103
|
+
|
104
|
+
|
105
|
+
## Contributing
|
106
|
+
|
107
|
+
If you have problems create an issue. Feedback is also welcome - you can write me on email or contact me on freenode (@mswart).
|
108
|
+
|
109
|
+
If you have a change which should be included into cany:
|
110
|
+
|
111
|
+
1. Fork it
|
112
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
113
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
114
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
115
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/cany
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
#require 'bundler/setup'
|
4
|
+
|
5
|
+
require 'cany'
|
6
|
+
require 'cany/dpkg/creator'
|
7
|
+
require 'cany/dpkg/builder'
|
8
|
+
|
9
|
+
tasks = {
|
10
|
+
'dpkg-create' => Cany::Dpkg::Creator,
|
11
|
+
'dpkg-builder' => Cany::Dpkg::Builder
|
12
|
+
}
|
13
|
+
|
14
|
+
unless tasks.include? ARGV.first
|
15
|
+
puts 'Unknown subaction - choose from ' + tasks.keys.join(', ')
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
|
19
|
+
tasks[ARGV.shift].new(Cany::setup '.').run *ARGV
|
data/cany.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cany/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'cany'
|
8
|
+
spec.version = Cany::VERSION
|
9
|
+
spec.authors = ['Malte Swart']
|
10
|
+
spec.email = %w(mswart@devtation.de)
|
11
|
+
spec.description = %q{Canning your ruby applications}
|
12
|
+
spec.summary = %q{Canning your ruby applications}
|
13
|
+
spec.homepage = 'https://github.com/mswart/cany'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = %w(lib)
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
end
|
data/lib/cany.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'cany/version'
|
2
|
+
require 'cany/specification'
|
3
|
+
require 'cany/recipe'
|
4
|
+
require 'cany/recipes/bundler'
|
5
|
+
require 'cany/recipes/rails'
|
6
|
+
require 'cany/recipes/web_server'
|
7
|
+
require 'cany/recipes/thin'
|
8
|
+
require 'cany/dpkg'
|
9
|
+
require 'cany/dpkg/creator'
|
10
|
+
require 'cany/dpkg/builder'
|
11
|
+
require 'cany/dpkg/deb_helper_recipe'
|
12
|
+
|
13
|
+
module Cany
|
14
|
+
class MissingSpecification < Exception
|
15
|
+
end
|
16
|
+
|
17
|
+
class MultipleSpecifications < Exception
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.setup(directory='.')
|
21
|
+
specs = Dir[directory + '/*.' + Specification::EXT]
|
22
|
+
raise MissingSpecification, "No #{Specification::EXT} found in #{directory}" if specs.size == 0
|
23
|
+
raise MultipleSpecifications, "Multiple #{Specification::EXT} found in #{directory}" if specs.size > 1
|
24
|
+
file = specs.first
|
25
|
+
spec = eval File::read(file), binding, file
|
26
|
+
spec.base_dir = directory
|
27
|
+
spec
|
28
|
+
end
|
29
|
+
end
|
data/lib/cany/dpkg.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Cany
|
2
|
+
module Dpkg
|
3
|
+
# This class does the real job. It is called for every dpkg build step to build the package.
|
4
|
+
# An instance is created on every execution. The package specification is passed as option to
|
5
|
+
# the initializer. Afterwards the run method is called to execute the wanted build step. The
|
6
|
+
# first parameter specifies the build step name (clean, build, binary).
|
7
|
+
# This method uses recipes instances to do the job itself.
|
8
|
+
class Builder
|
9
|
+
attr_reader :spec
|
10
|
+
def initialize(spec)
|
11
|
+
@spec = spec
|
12
|
+
end
|
13
|
+
|
14
|
+
def debian(*args)
|
15
|
+
File.join spec.base_dir, 'debian', *args
|
16
|
+
end
|
17
|
+
|
18
|
+
# This method is called to do the actual work
|
19
|
+
# @api public
|
20
|
+
# @param [String] build_step_name The name of the dpkg build step (clean, build, binary)
|
21
|
+
def run(build_step_name)
|
22
|
+
setup_recipes
|
23
|
+
@recipes.first.send build_step_name.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
# This method creates recipe instances for all required recipes from the given spec.
|
28
|
+
def setup_recipes
|
29
|
+
@recipes = []
|
30
|
+
@recipes << DebHelperRecipe.new(spec, nil)
|
31
|
+
spec.recipes.reverse.each do |name|
|
32
|
+
@recipes.unshift Recipe.from_name(name).new(spec, @recipes.first)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Cany
|
4
|
+
module Dpkg
|
5
|
+
class Creator
|
6
|
+
attr_reader :spec
|
7
|
+
def initialize(spec)
|
8
|
+
@spec = spec
|
9
|
+
@options = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def debian(*args)
|
13
|
+
File.join spec.base_dir, 'debian', *args
|
14
|
+
end
|
15
|
+
|
16
|
+
def ruby_exe
|
17
|
+
@options[:ruby_exe] || 'ruby'
|
18
|
+
end
|
19
|
+
|
20
|
+
def ruby_deb
|
21
|
+
@options[:ruby_deb] || 'ruby'
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_opts(*args)
|
25
|
+
@options = {}
|
26
|
+
|
27
|
+
OptionParser.new do|opts|
|
28
|
+
opts.on('--ruby-exe RUBY_EXE', 'Choose the ruby interpreter name (default is ruby)') do |exe|
|
29
|
+
@options[:ruby_exe] = exe
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('--ruby-deb RUBY_DEB', 'Choose the ruby package name (default is ruby)') do |deb|
|
33
|
+
@options[:ruby_deb] = deb
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on('-r', '--ruby RUBY_EXE_DEB', 'Set ruby interpreter and package name') do |name|
|
37
|
+
@options[:ruby_exe] = name
|
38
|
+
@options[:ruby_deb] = name
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on('-h', '--help', 'Display this screen') do
|
42
|
+
puts opts
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
end.parse *args
|
46
|
+
end
|
47
|
+
|
48
|
+
def run(*args)
|
49
|
+
parse_opts *args
|
50
|
+
|
51
|
+
Dir.mkdir debian
|
52
|
+
create_compact
|
53
|
+
create_source_format
|
54
|
+
create_source_control
|
55
|
+
create_copyright
|
56
|
+
create_rules
|
57
|
+
create_changelog
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_source_format
|
61
|
+
Dir.mkdir debian 'source'
|
62
|
+
File.open(debian('source', 'format'), 'w') { |f| f.write '3.0 (native)' }
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_compact
|
66
|
+
File.open debian('compat'), 'w' do |f|
|
67
|
+
f.write '8'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_source_control
|
72
|
+
require 'bundler'
|
73
|
+
lock_path = File.join(spec.base_dir, 'Gemfile.lock')
|
74
|
+
extra_libs = {
|
75
|
+
pg: ['libpq-dev', 'libpq5']
|
76
|
+
}
|
77
|
+
src_deps = ['debhelper (>= 7.0.50~)', ruby_deb, ruby_deb + '-dev']
|
78
|
+
bin_deps = ['${shlibs:Depends}', '${misc:Depends}', ruby_deb]
|
79
|
+
if File.exists? lock_path
|
80
|
+
lock = Bundler::LockfileParser.new File.read lock_path
|
81
|
+
lock.specs.each do |spec|
|
82
|
+
if extra_libs.has_key? spec.name.to_sym
|
83
|
+
src, bin = extra_libs[spec.name.to_sym]
|
84
|
+
src_deps << src
|
85
|
+
bin_deps << bin
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
File.open debian('control'), 'w' do |f|
|
90
|
+
# write source package fields:
|
91
|
+
f.write("Source: #{spec.name}\n")
|
92
|
+
f.write("Section: ruby\n")
|
93
|
+
f.write("Priority: optional\n")
|
94
|
+
f.write("Maintainer: #{spec.maintainer_name} <#{spec.maintainer_email}>\n")
|
95
|
+
f.write("Standards-Version: 3.9.2\n")
|
96
|
+
f.write("Homepage: #{spec.website}\n")
|
97
|
+
f.write("Build-Depends: #{src_deps.join(', ')}\n")
|
98
|
+
|
99
|
+
# write binary package fields:
|
100
|
+
f.write("\n")
|
101
|
+
f.write("Package: #{spec.name}\n")
|
102
|
+
f.write("Architecture: any\n")
|
103
|
+
f.write("Depends: #{bin_deps.join(', ')}\n")
|
104
|
+
f.write("Description: #{spec.description}\n")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def create_copyright
|
109
|
+
File.open debian('copyright'), 'w' do |f|
|
110
|
+
f.write("Format: http://dep.debian.net/deps/dep5\n")
|
111
|
+
f.write("Upstream-Name: #{spec.name}\n\n")
|
112
|
+
|
113
|
+
f.write("Files: *\n")
|
114
|
+
f.write("Copyright: #{Time.now.year} #{spec.maintainer_name}\n")
|
115
|
+
f.write("Licence: #{spec.licence}\n [LICENCE TEXT]\n")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def create_rules
|
120
|
+
File.open debian('rules'), 'w' do |f|
|
121
|
+
f.write("#!/usr/bin/make -f\n")
|
122
|
+
f.write("export PATH := debian/bin:${PATH}\n")
|
123
|
+
f.write("export GEM_PATH := debian/gems:${GEM_PATH}\n")
|
124
|
+
# call cany for every target:
|
125
|
+
f.write("%:\n")
|
126
|
+
f.write("\t#{ruby_exe} -cS cany >/dev/null || #{ruby_exe} -S gem install --no-ri --no-rdoc --install-dir debian/gems --bindir debian/bin $${CANY_GEM:-cany}\n")
|
127
|
+
f.write("\t#{ruby_exe} -S cany dpkg-builder $@\n")
|
128
|
+
f.write("\noverride_dh_prep:\n")
|
129
|
+
|
130
|
+
f.chmod(0755)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def create_changelog
|
135
|
+
File.open debian('changelog'), 'w' do |f|
|
136
|
+
f.write "#{spec.name} (#{spec.version}-1) unstable; urgency=low\n"
|
137
|
+
f.write "\n"
|
138
|
+
f.write " * Build with cany\n"
|
139
|
+
f.write "\n"
|
140
|
+
f.write " -- #{spec.maintainer_name} <#{spec.maintainer_email}> #{Time.now.strftime "%a, %d %b %Y %H:%M:%S %z" }"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|