fpm-cookery 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ cache/
6
+ pkg/
7
+ tmp-build/
8
+ tmp-dest/
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ # v0.0.1
2
+ * Initial gem release.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # fpm-cookery - For building software
2
+
3
+ A tool for building software packages with
4
+ [fpm](https://github.com/jordansissel/fpm).
5
+
6
+ The [fpm](https://github.com/jordansissel/fpm) project is really nice for
7
+ building operating system packages like `.deb` and `.rpm`. But it only helps
8
+ you to create the packages and doesn't help you with actually building the
9
+ software.
10
+
11
+ __fpm-cookery__ provides an infrastructure to automatically build software
12
+ based on recipes. It's heavily inspired and borrows code from the great
13
+ [homebrew](https://github.com/mxcl/homebrew) and
14
+ [brew2deb](https://github.com/tmm1/brew2deb) projects.
15
+ The [OpenBSD Ports System](http://www.openbsd.org/faq/ports/index.html) is
16
+ probably another source of inspiration since I've been working with that for
17
+ quite some time
18
+
19
+ It is using __fpm__ to create the actual packages.
20
+
21
+ ## Why?
22
+
23
+ Building operating system packages for Debian/Ubuntu and RedHat using the
24
+ official process and tools is pretty annoying if you just want some custom
25
+ packages. Jordan's [fpm](https://github.com/jordansissel/fpm) removes the
26
+ biggest hurdle by providing a simple command line tool to build packages
27
+ for different operating systems.
28
+
29
+ Before you can use __fpm__ to create the package, you have to build the software,
30
+ though. In the past I've been using some shell scripts and Makefiles to
31
+ automate this task.
32
+
33
+ Then I discovered Aman's [brew2deb](https://github.com/tmm1/brew2deb) which is
34
+ actually [homebrew](https://github.com/mxcl/homebrew) with some modifications
35
+ to make it work on Linux. (only Debian/Ubuntu for now) Since __homebrew__ was
36
+ designed for Mac OS X, I thought it would be nice to have a "native" Linux
37
+ tool for the job.
38
+
39
+ __fpm-cookery__ is my attempt to build such a tool.
40
+
41
+ ## Features
42
+
43
+ * Download of the source archives. (via __curl(1)__)
44
+ * Recipes to describe and execute the software build.
45
+ (e.g. configure, make, make install)
46
+ * Sandboxed builds.
47
+ * Package creation via __fpm__.
48
+ * Standalone recipe trees/books/you name it. No need to put the recipes into
49
+ the __fpm-cookery__ source tree.
50
+
51
+ ## Upcoming Features
52
+
53
+ * Apply custom patches.
54
+ * Dependency checking.
55
+ * Recipe validation.
56
+ * Integrity checks for downloaded archives.
57
+ * More source types. (git, svn, ...)
58
+ * Progress output and logging.
59
+ * Make package output type configurable. (deb, rpm)
60
+ * Extend recipe features and build/install helpers.
61
+ * Configuration file. (for stuff like vendor and maintainer)
62
+ * Options for the `fpm-cook` command.
63
+ * Manpage for the `fpm-cook` command.
64
+
65
+ ## Getting Started
66
+
67
+ Since there is no gem available yet, you have to clone the repository to
68
+ your local machine and run the following to build a recipe.
69
+
70
+ $ ruby bin/fpm-cook recipes/redis/recipe.rb clean
71
+ $ ruby bin/fpm-cook recipes/redis/recipe.rb
72
+
73
+ Or change into the recipe directory.
74
+
75
+ $ export PATH="$PWD/bin:$PATH"
76
+ $ cd recipes/redis
77
+ $ fpm-cook clean
78
+ $ fpm-cook
79
+
80
+ You can run the included test suite with `rake test`. This needs the __rake__
81
+ and __minitest__ gems.
82
+
83
+ ## Status
84
+
85
+ It can build the included `recipes/redis/recipe.rb` and
86
+ `recipes/nodejs/recipe.rb` recipes. (both imported from __brew2deb__)
87
+ See __CAVEATS__ for an incomplete list of missing stuff.
88
+
89
+ ## Example Recipe
90
+
91
+ The following is an example recipe. I have some more in my recipe collection
92
+ [over here](https://github.com/bernd/fpm-recipes).
93
+
94
+ ```ruby
95
+ class Redis < FPM::Cookery::Recipe
96
+ homepage 'http://redis.io'
97
+ source 'http://redis.googlecode.com/files/redis-2.2.5.tar.gz'
98
+ md5 'fe6395bbd2cadc45f4f20f6bbe05ed09'
99
+
100
+ name 'redis-server'
101
+ version '2.2.5'
102
+ revision '1'
103
+
104
+ description 'An advanced key-value store.'
105
+
106
+ conflicts 'redis-server'
107
+
108
+ config_files '/etc/redis/redis.conf'
109
+
110
+ def build
111
+ make
112
+
113
+ inline_replace 'redis.conf' do |s|
114
+ s.gsub! 'daemonize no', 'daemonize yes'
115
+ end
116
+ end
117
+
118
+ def install
119
+ # make :install, 'DESTDIR' => destdir
120
+
121
+ var('lib/redis').mkdir
122
+
123
+ %w(run log/redis).each {|p| var(p).mkdir }
124
+
125
+ bin.install ['src/redis-server', 'src/redis-cli']
126
+
127
+ etc('redis').install 'redis.conf'
128
+ etc('init.d').install 'redis-server.init.d' => 'redis-server'
129
+ end
130
+ end
131
+ ```
132
+
133
+ ## CAVEATS
134
+
135
+ * At the moment, there's only a small subset of the __homebrew__ DSL implemented.
136
+ * No recipe documentation and API documentation yet.
137
+ * No recipe validation yet.
138
+ * No dependency validation yet.
139
+ * No integrity check of the downloaded archives yet.
140
+ * No support for patches yet.
141
+ * Only simple source/url types (via curl) for now.
142
+ * No real logging output yet.
143
+ * No gem on rubygems.org yet.
144
+ * Pretty new and not well tested.
145
+
146
+ ## Credits
147
+
148
+ __fpm-cookery__ borrows lots of __ideas__ and also __code__ from the
149
+ [homebrew](https://github.com/mxcl/homebrew) and
150
+ [brew2deb](https://github.com/tmm1/brew2deb) projects. Both projects don't
151
+ have any licensing information included in their repositories. So licensing
152
+ is still an open question for now.
153
+
154
+ ## How To Contribute
155
+
156
+ * I'd love to hear if you like it, hate it, use it and if you have suggestions
157
+ and/or problems.
158
+ * Send pull requests. (hugs for topic branches and tests)
159
+ * Have fun!
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'rake/testtask'
2
+ require 'bundler/gem_tasks'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = "spec/*_spec.rb"
6
+ t.libs << 'spec'
7
+ t.verbose = false
8
+ end
9
+
10
+ task :default => :test
data/bin/fpm-cook ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'fpm/cookery/cli'
6
+
7
+ FPM::Cookery::CLI.new(ARGV).run
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "fpm/cookery/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "fpm-cookery"
7
+ s.version = FPM::Cookery::VERSION
8
+ s.authors = ["Bernd Ahlers"]
9
+ s.email = ["bernd@tuneafish.de"]
10
+ s.homepage = ""
11
+ s.summary = %q{A tool for building software packages with fpm.}
12
+ s.description = %q{A tool for building software packages with fpm.}
13
+
14
+ s.rubyforge_project = "fpm-cookery"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "fpm"
22
+ s.add_development_dependency "minitest"
23
+ s.add_runtime_dependency "fpm"
24
+ end
@@ -0,0 +1,16 @@
1
+ module FPM
2
+ module Cookery
3
+ class Book
4
+ # Load the given file and instantiate an object. Wrap the class in an
5
+ # anonymous module to avoid namespace cluttering. (see Kernel.load)
6
+ def self.load_recipe(filename, &callback)
7
+ Kernel.load(filename, true)
8
+ callback.call(@recipe.new(filename))
9
+ end
10
+
11
+ def self.loaded_recipe(klass)
12
+ @recipe = klass
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'fpm/cookery/book'
2
+
3
+ module FPM
4
+ module Cookery
5
+ module BookHook
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def inherited(klass)
12
+ FPM::Cookery::Book.loaded_recipe(klass)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ require 'fpm/cookery/book_hook'
2
+ require 'fpm/cookery/recipe'
3
+ require 'fpm/cookery/packager'
4
+
5
+ module FPM
6
+ module Cookery
7
+ class CLI
8
+ def initialize(argv)
9
+ @argv = argv
10
+ end
11
+
12
+ def run
13
+ filename = @argv.find {|arg| arg =~ /\.rb$/ and File.exists?(arg) }
14
+ filename ||= File.expand_path('recipe.rb')
15
+
16
+ unless File.exists?(filename)
17
+ STDERR.puts 'No recipe.rb found in the current directory, abort.'
18
+ exit 1
19
+ end
20
+
21
+ FPM::Cookery::Recipe.send(:include, FPM::Cookery::BookHook)
22
+
23
+ FPM::Cookery::Book.load_recipe(filename) do |recipe|
24
+ packager = FPM::Cookery::Packager.new(recipe)
25
+
26
+ if @argv.include?('clean')
27
+ packager.cleanup
28
+ else
29
+ packager.dispense
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,155 @@
1
+ #require 'digest/md5'
2
+ #require 'fpm/cookery/recipe_inspector'
3
+ #require 'fpm/cookery/dependency_inspector'
4
+ require 'fpm/cookery/utils'
5
+
6
+ module FPM
7
+ module Cookery
8
+ class Packager
9
+ include FPM::Cookery::Utils
10
+
11
+ attr_reader :recipe, :config
12
+
13
+ def initialize(recipe, config = {})
14
+ @recipe = recipe
15
+ @config = config
16
+ end
17
+
18
+ def cleanup
19
+ FileUtils.rm_rf(recipe.builddir)
20
+ FileUtils.rm_rf(recipe.destdir)
21
+ end
22
+
23
+ def dispense
24
+ env = ENV.to_hash
25
+
26
+ # RecipeInspector.verify!(recipe)
27
+ # DependencyInspector.verify!(recipe.depends, recipe.build_depends)
28
+
29
+ recipe.installing = false
30
+
31
+ source = recipe.source_handler
32
+
33
+ recipe.cachedir.mkdir
34
+ Dir.chdir(recipe.cachedir) do
35
+ source.fetch
36
+
37
+ #check = Source::IntegrityCheck.new(source.filename, recipe.md5, Digest::MD5)
38
+
39
+ #if check.error?
40
+ # check.actual, check.expected, check.filename, check.digest
41
+ #end
42
+ end
43
+
44
+ recipe.builddir.mkdir
45
+ Dir.chdir(recipe.builddir) do
46
+ extracted_source = source.extract
47
+
48
+ Dir.chdir(extracted_source) do
49
+ #Source::Patches.new(recipe.patches).apply!
50
+
51
+ build_cookie = build_cookie_name("#{recipe.name}-#{recipe.version}")
52
+
53
+ if File.exists?(build_cookie)
54
+ STDERR.puts 'Skipping build (`cook clean` to rebuild)'
55
+ else
56
+ recipe.build and FileUtils.touch(build_cookie)
57
+ end
58
+
59
+ FileUtils.rm_rf(recipe.destdir)
60
+ recipe.destdir.mkdir
61
+
62
+ begin
63
+ recipe.installing = true
64
+ recipe.install
65
+ ensure
66
+ recipe.installing = false
67
+ end
68
+ end
69
+ end
70
+
71
+ build_package(recipe, config)
72
+ ensure
73
+ # Make sure we reset the environment.
74
+ ENV.replace(env)
75
+ end
76
+
77
+ def build_cookie_name(name)
78
+ (recipe.builddir/".build-cookie-#{name.gsub(/[^\w]/,'_')}").to_s
79
+ end
80
+
81
+ def build_package(recipe, config)
82
+ recipe.pkgdir.mkdir
83
+ Dir.chdir(recipe.pkgdir) do
84
+ epoch, ver = recipe.version.split(':', 2)
85
+ if ver.nil?
86
+ ver, epoch = epoch, nil
87
+ end
88
+
89
+ # Build a version including vendor and revision.
90
+ vendor = config[:vendor] || recipe.vendor
91
+ vendor_rev = "#{vendor}#{recipe.revision}"
92
+ version = [ver, vendor_rev].join('+')
93
+
94
+ maintainer = recipe.maintainer || begin
95
+ username = `git config --get user.name`.strip
96
+ useremail = `git config --get user.email`.strip
97
+ raise 'Set maintainer name/email via `git config --global user.name <name>`' if username.empty?
98
+ "#{username} <#{useremail}>"
99
+ end
100
+
101
+ opts = [
102
+ '-n', recipe.name,
103
+ '-v', version,
104
+ '-t', 'deb',
105
+ '-s', 'dir',
106
+ '--url', recipe.homepage || recipe.url,
107
+ '-C', recipe.destdir,
108
+ '--maintainer', maintainer,
109
+ '--category', recipe.section || 'optional',
110
+ ]
111
+
112
+ opts += [
113
+ '--epoch', epoch
114
+ ] if epoch
115
+
116
+ opts += [
117
+ '--description', recipe.description.strip
118
+ ] if recipe.description
119
+
120
+ opts += [
121
+ '--architecture', recipe.arch.to_s
122
+ ] if recipe.arch
123
+
124
+ # if self.postinst
125
+ # postinst_file = Tempfile.open('postinst')
126
+ # postinst_file.puts(postinst)
127
+ # chmod 0755, postinst_file.path
128
+ # postinst_file.close
129
+ # opts += ['--post-install', postinst_file.path]
130
+ # end
131
+ # if self.postrm
132
+ # postrm_file = Tempfile.open('postrm')
133
+ # postrm_file.puts(postrm)
134
+ # chmod 0755, postrm_file.path
135
+ # postrm_file.close
136
+ # opts += ['--post-uninstall', postrm_file.path]
137
+ # end
138
+
139
+ %w[ depends exclude provides replaces conflicts config_files ].each do |type|
140
+ if recipe.send(type).any?
141
+ recipe.send(type).each do |dep|
142
+ opts += ["--#{type.gsub('_','-')}", dep]
143
+ end
144
+ end
145
+ end
146
+
147
+ opts << '.'
148
+
149
+ STDERR.puts ['fpm', opts].flatten.inspect
150
+ safesystem(*['fpm', opts].flatten)
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end