fpm-cookery 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.
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