rake-plus 0.5

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/README.md ADDED
@@ -0,0 +1,13 @@
1
+ Rake ext
2
+ ========
3
+
4
+ A couple of rake extensions that are useful for building stuff.
5
+
6
+ I always end up needing these things so here they are, neatly packaged
7
+ together.
8
+
9
+ One of the differenciating approach here is that new kind of tasks are
10
+ implemented a new "types" of tasks instead as being exposed like
11
+ class derivatives.
12
+
13
+ Check out the example to see what I mean.
@@ -0,0 +1,49 @@
1
+ require 'rake-plus/sudo'
2
+ require 'rake/task'
3
+
4
+ module Rake
5
+ # TODO: abstract dependencies from the package system
6
+ class BuildDependency < Rake::Task
7
+ include Rake::DSL
8
+ ON_OSX = (`uname` == "Darwin\n")
9
+
10
+ def needed?
11
+ return if ON_OSX
12
+ # dpkg-query also returns old packages, so '^ii ' guarantees only installed
13
+ sh("dpkg-query -l \"#{basename}\" | grep -e '^ii ' > /dev/null 2>&1") do |ok, res|
14
+ return :package_missing unless ok
15
+ end
16
+ return
17
+ end
18
+
19
+ def timestamp
20
+ Rake::EARLY
21
+ end
22
+
23
+ def execute(args=nil)
24
+ sudo "apt-get install -qy \"#{basename}\""
25
+ super
26
+ end
27
+
28
+ def basename
29
+ name.sub(':bdep:','')
30
+ end
31
+
32
+ class << self
33
+ # Apply the scope to the task name according to the rules for this kind
34
+ # of task. File based tasks ignore the scope when creating the name.
35
+ def scope_name(scope, task_name)
36
+ ":bdep:#{task_name}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def bdep(args, &block)
43
+ t = Rake::BuildDependency.define_task(args, &block)
44
+ Rake::Task["bdeps"].prerequisites.push(t.name).uniq!
45
+ t
46
+ end
47
+
48
+ desc "Installs all bdeps"
49
+ task "bdeps"
@@ -0,0 +1,97 @@
1
+ require 'uri'
2
+
3
+ module Rake
4
+ class GitDependency < Rake::Task
5
+ include Rake::DSL if defined? Rake::DSL
6
+ attr_accessor :branch, :commit_id
7
+
8
+ def branch(val = nil)
9
+ @branch = val if val
10
+ @branch
11
+ end
12
+
13
+ def commit_id(val = nil)
14
+ @commit_id = val if val
15
+ @commit_id
16
+ end
17
+
18
+ def commit_or_branch
19
+ @commit_id || @branch
20
+ end
21
+
22
+ def needed?
23
+ return :checkout_missing unless File.directory?(local_path)
24
+ # check if that revision exists
25
+ if commit_or_branch
26
+ Dir.chdir(local_path) do
27
+ sh("git log #{commit_or_branch} -- >/dev/null 2>&1") do |ok, res|
28
+ return "commit_missing: #{commit_or_branch}" unless ok
29
+ end
30
+ end
31
+ end
32
+ return
33
+ end
34
+
35
+ def timestamp
36
+ if File.exist?(local_path / 'HEAD')
37
+ File.mtime(local_path / 'HEAD')
38
+ else
39
+ Rake::EARLY
40
+ end
41
+ end
42
+
43
+ def local_path
44
+ RakePlus.cache_dir / "git" / basename
45
+ end
46
+
47
+ def basename
48
+ base = File.basename(name)
49
+ unless File.extname(base) == ".git"
50
+ base + ".git"
51
+ else
52
+ base
53
+ end
54
+ end
55
+
56
+ def execute(args=nil)
57
+ if File.directory?(local_path)
58
+ Dir.chdir(local_path) do
59
+ sh "git remote update"
60
+ end
61
+ else
62
+ FileUtils.mkdir_p File.dirname(local_path)
63
+ sh "git clone --mirror #{name} #{local_path}"
64
+ end
65
+ super
66
+ end
67
+
68
+ # Utility
69
+ def clone_to(dir, branch=nil)
70
+ sh "git clone #{local_path} #{dir}"
71
+ if commit_or_branch
72
+ Dir.chdir(dir) do
73
+ sh "git checkout #{commit_or_branch}"
74
+ end
75
+ end
76
+ end
77
+
78
+ # Like .tar archives, creates a folder beneath it
79
+ def unpack_to(dir)
80
+ clone_to(File.join(dir, basename))
81
+ end
82
+
83
+ class << self
84
+ # Git based tasks ignore the scope when creating the name.
85
+ def scope_name(scope, task_name); task_name end
86
+ end
87
+ end
88
+ end
89
+
90
+ # Usage: git("git@github.com:zimbatm/direnv.git")
91
+ #
92
+
93
+
94
+ def git(*args, &block)
95
+ Rake::GitDependency.define_task(*args, &block)
96
+ end
97
+ alias git_dep git
@@ -0,0 +1,10 @@
1
+ require 'time'
2
+
3
+ def gitver(branch_or_file=nil)
4
+ ret = `git log -n 1 --oneline #{branch_or_file}`
5
+ raise ArgumentError, "could not find #{branch_or_file}" if $?.exitstatus > 0
6
+ raise "parse error" unless (ret =~ /^([^\s]+)/)
7
+ commit_id = $1
8
+ commit_num = `git log --oneline #{branch_or_file} | wc -l`.chomp.to_i
9
+ "#{commit_num}-#{commit_id}"
10
+ end
@@ -0,0 +1,90 @@
1
+ require 'pathname'
2
+
3
+ # Patching Pathame to return self.class.new objects instead of Pathname.new
4
+ class Pathname
5
+ def +(other)
6
+ other = self.class.new(other) unless other.kind_of?(Pathname)
7
+ self.class.new(plus(@path, other.to_s))
8
+ end
9
+
10
+ def join(*args)
11
+ args.unshift self
12
+ result = args.pop
13
+ result = self.class.new(result) unless result.kind_of?(Pathname)
14
+ return result if result.absolute?
15
+ args.reverse_each {|arg|
16
+ arg = self.class.new(arg) unless arg.kind_of?(Pathname)
17
+ result = arg + result
18
+ return result if result.absolute?
19
+ }
20
+ result
21
+ end
22
+
23
+ def relative_path_from(base_directory)
24
+ dest_directory = self.cleanpath.to_s
25
+ base_directory = base_directory.cleanpath.to_s
26
+ dest_prefix = dest_directory
27
+ dest_names = []
28
+ while r = chop_basename(dest_prefix)
29
+ dest_prefix, basename = r
30
+ dest_names.unshift basename if basename != '.'
31
+ end
32
+ base_prefix = base_directory
33
+ base_names = []
34
+ while r = chop_basename(base_prefix)
35
+ base_prefix, basename = r
36
+ base_names.unshift basename if basename != '.'
37
+ end
38
+ unless SAME_PATHS[dest_prefix, base_prefix]
39
+ raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
40
+ end
41
+ while !dest_names.empty? &&
42
+ !base_names.empty? &&
43
+ SAME_PATHS[dest_names.first, base_names.first]
44
+ dest_names.shift
45
+ base_names.shift
46
+ end
47
+ if base_names.include? '..'
48
+ raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
49
+ end
50
+ base_names.fill('..')
51
+ relpath_names = base_names + dest_names
52
+ if relpath_names.empty?
53
+ self.class.new('.')
54
+ else
55
+ self.class.new(File.join(*relpath_names))
56
+ end
57
+ end
58
+ end
59
+
60
+ # Like a Pathname but behaves more like a string
61
+ class Path < Pathname
62
+ alias / +
63
+ def =~(regex); to_s =~ regex end
64
+ def <=>(other); to_s <=> other.to_s end
65
+ def length; to_s.length end
66
+ alias size length
67
+
68
+ def method_missing(m, *a, &b)
69
+ s = to_s
70
+ if s.respond_to? m
71
+ s.send(m, *a, &b)
72
+ else
73
+ super
74
+ end
75
+ end
76
+
77
+ def respond_to?(m)
78
+ super || to_s.respond_to?(m)
79
+ end
80
+
81
+ alias expand expand_path
82
+
83
+ def self.[](*args)
84
+ new File.join(*args.map(&:to_s))
85
+ end
86
+ end
87
+
88
+ def path(*args)
89
+ Path[ *args ]
90
+ end
@@ -0,0 +1,12 @@
1
+ module Rake
2
+ class Task
3
+ # Are there any prerequisites with a later time than the given time stamp?
4
+ def out_of_date?(stamp)
5
+ @prerequisites.any? { |n| application[n, @scope].timestamp > stamp}
6
+ end
7
+
8
+ def file_missing?(path)
9
+ File.exist?(path) ? nil : :file_missing
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,221 @@
1
+ require 'rake-plus'
2
+ require 'rake-plus/rake_ext'
3
+
4
+ class Recipe < Rake::Task
5
+ include Rake::DSL if defined? Rake::DSL
6
+ BUILD_SYSTEM = `uname -sm`.strip.gsub(' ', '-').downcase
7
+
8
+ @@defaults = {}
9
+
10
+ class << self
11
+ def var(name, &default)
12
+ if block_given?
13
+ @@defaults[name.to_s] = default
14
+
15
+ class_eval <<-CODE
16
+ def #{name}(val=nil)
17
+ if !val.nil?
18
+ @#{name} = val
19
+ elsif @#{name}.nil?
20
+ @#{name} = instance_eval(&@@defaults["#{name}"])
21
+ end
22
+ @#{name}
23
+ end
24
+ CODE
25
+ else
26
+ class_eval <<-CODE
27
+ def #{name}(val=nil)
28
+ @#{name} = val unless val.nil?
29
+ @#{name}
30
+ end
31
+ CODE
32
+ end
33
+ self
34
+ end
35
+ end
36
+
37
+ # Variables for the DSL
38
+ def url_or_git
39
+ @url || @git || @svn
40
+ end
41
+
42
+ var :unpack_dir do
43
+ url_or_git.basename.sub('.tar.gz','').sub('.tar.bz2','')
44
+ end
45
+ var :build_dir do
46
+ # : causes trouble in PATH
47
+ sub = name.split(':')
48
+ sub[0] = sub[0] + '-' + BUILD_SYSTEM
49
+ RakePlus.build_dir / sub.join('/')
50
+ end
51
+ var :src_dir do
52
+ build_dir / 'src'
53
+ end
54
+ var :dep_dir do
55
+ build_dir / 'dep'
56
+ end
57
+ var :prefix do
58
+ build_dir / 'install'
59
+ end
60
+ def bdeps(val=nil)
61
+ unless val.nil?
62
+ val = [val] unless val.kind_of? Array
63
+ @bdeps = val.map{|x| x.kind_of?(Recipe) ? x : recipe(x)}
64
+ end
65
+ @bdeps || []
66
+ end
67
+ var :static_install
68
+ def install(&block)
69
+ @install ||= block
70
+ @install ||= proc do
71
+ mkdir_p(prefix)
72
+ sh "./configure --prefix=#{prefix}#{static_install ? " --enable-static --disable-shared" : ''}"
73
+ sh "make"
74
+ sh "make install"
75
+ end
76
+ @install
77
+ end
78
+ def url(val=nil)
79
+ unless val.nil?
80
+ @url = remote_package(val)
81
+ end
82
+ @url
83
+ end
84
+ def git(val=nil)
85
+ unless val.nil?
86
+ @git = git_dep(val)
87
+ end
88
+ @git
89
+ end
90
+
91
+ def svn(val=nil)
92
+ unless val.nil?
93
+ @svn = svn_dep(val)
94
+ end
95
+ @svn
96
+ end
97
+
98
+ def patches
99
+ @patches ||= []
100
+ end
101
+ def patch(val)
102
+ patches.push(val)
103
+ end
104
+
105
+ def install_to(dir)
106
+ sh "cp -r #{prefix}/* #{dir}"
107
+ end
108
+
109
+ def needed?
110
+ file_missing?(build_dir / "install.done") || out_of_date?(timestamp)
111
+ end
112
+
113
+ def timestamp
114
+ if File.exist?(build_dir / "install.done")
115
+ File.mtime(build_dir / "install.done")
116
+ else
117
+ Rake::EARLY
118
+ end
119
+ end
120
+
121
+ def opts_to_string(opts)
122
+ ary = []
123
+ opts.each_pair do |k,v|
124
+ ary << "--#{k}=#{v}"
125
+ end
126
+ ary.join(' ')
127
+ end
128
+
129
+ def flags_to_features(*flags)
130
+ flags.flatten.map do |x|
131
+ case x
132
+ when /^\+/
133
+ x.gsub(/^\+/,'--enable-')
134
+ when /^-/
135
+ x.gsub(/^-/,'--disable-')
136
+ when /^\/\//, /^\s*$/
137
+ nil
138
+ else
139
+ raise ArgumentError, "unknown flag: #{x}"
140
+ end
141
+ end.compact.join(' ')
142
+ end
143
+
144
+ def define(&block)
145
+ raise "Already defined" if @defined
146
+ instance_eval(&block)
147
+ raise "Missing @url or @git" unless url_or_git
148
+
149
+ directory build_dir
150
+ directory src_dir
151
+
152
+ patches.each{|p| file(p) }
153
+
154
+
155
+ file(build_dir / "src.done" => [url_or_git, build_dir, src_dir] + patches) do |t|
156
+ sh "rm -rf #{src_dir}/*"
157
+ url_or_git.unpack_to src_dir
158
+
159
+ # Apply patches
160
+ Dir.chdir(src_dir / unpack_dir) do
161
+ patches.each do |p|
162
+ sh "patch -b -f -N -p0 < #{RakePlus.top / p}"
163
+ end
164
+ end
165
+
166
+ touch t.name
167
+ end
168
+
169
+ self.class.define_task(self.name.sub(/.*:/,'') => bdeps + [build_dir / "src.done"] + Dir[src_dir/'**/*'] )
170
+
171
+ @defined = true
172
+ self
173
+ end
174
+
175
+ def execute(args=nil)
176
+ raise "Not defined" unless @defined
177
+
178
+ # Make build-dependencies available
179
+ if bdeps.any?
180
+ rm_rf dep_dir
181
+ mkdir_p dep_dir
182
+ bdeps.each{|dep| recipe(dep).install_to(dep_dir) }
183
+ end
184
+
185
+ with_env(
186
+ :LDFLAGS => "-L#{dep_dir}/lib",
187
+ :CFLAGS => "-I#{dep_dir}/include",
188
+ :PKG_CONFIG_PATH => "#{dep_dir}/lib/pkgconfig",
189
+ :PATH => "#{dep_dir}/bin:#{ENV['PATH']}"
190
+ ) do
191
+ Dir.chdir(src_dir / unpack_dir, &install)
192
+ end
193
+
194
+ # remove unused stuff
195
+ rm_rf prefix / "man"
196
+ rm_rf prefix / "share" / "man"
197
+ sh "find #{prefix} -type d -empty -delete"
198
+
199
+ # done
200
+ touch build_dir / "install.done"
201
+ super
202
+ end
203
+
204
+ protected
205
+
206
+ def with_env(new_env={}, &block)
207
+ bak = new_env.keys.inject({}) do |h,k|
208
+ h[k.to_s] = ENV[k.to_s]
209
+ h
210
+ end
211
+ new_env.each{|k,v| ENV[k.to_s] = v.to_s }
212
+ yield
213
+ ensure
214
+ bak.each{|k,v| ENV[k] = v }
215
+ end
216
+
217
+ end
218
+
219
+ def recipe(*args, &block)
220
+ Recipe.define_task(*args, &block)
221
+ end
@@ -0,0 +1,79 @@
1
+ module Rake
2
+ class RemotePackage < Rake::Task
3
+ include Rake::DSL if defined? Rake::DSL
4
+
5
+ # Example: https://github.com/vivienschilis/segmenter/zipball/v0.0.1
6
+ GITHUB_MATCH = %r[^https?://github\.com/([^/]+)/([^/]+)/(zip|tar)ball/(.*)]
7
+ SF_MATCH = %r[^https?://sourceforge.net/.*/([^/]+)/download$]
8
+
9
+ def needed?
10
+ :package_missing unless File.exist?(local_path)
11
+ end
12
+
13
+ def local_path
14
+ RakePlus.cache_dir / "pkg" / basename
15
+ end
16
+
17
+ def basename
18
+ if name =~ GITHUB_MATCH
19
+ "#{$1}-#{$2}-#{$4}#{$3 == "zip" ? ".zip" : ".tar.gz"}"
20
+ elsif name =~ SF_MATCH
21
+ $1
22
+ else
23
+ File.basename(name.sub(/\?.*/,''))
24
+ end
25
+ end
26
+
27
+ def execute(args=nil)
28
+ if needed?
29
+ FileUtils.mkdir_p File.dirname(local_path)
30
+ sh "curl -L -o \"#{local_path}.tmp\" \"#{name}\""
31
+ mv "#{local_path}.tmp", local_path
32
+ end
33
+ super
34
+ end
35
+
36
+ # Utility
37
+ def unpack_to(dir)
38
+ abs_local_path = RakePlus.top_dir.expand(local_path)
39
+ Dir.chdir(dir) do
40
+ case abs_local_path
41
+ when /\.tar\.gz$/, /\.tgz$/
42
+ sh "tar xzvf \"#{abs_local_path}\""
43
+ when /\.tar\.bz2$/
44
+ sh "tar xjvf \"#{abs_local_path}\""
45
+ when /\.tar$/
46
+ sh "tar xvf \"#{abs_local_path}\""
47
+ else
48
+ raise "Unsupported file extensions of #{basename}"
49
+ end
50
+ end
51
+ end
52
+
53
+ # Time stamp for file task.
54
+ def timestamp
55
+ if File.exist?(local_path)
56
+ File.mtime(local_path)
57
+ else
58
+ Rake::EARLY
59
+ end
60
+ end
61
+
62
+ class << self
63
+ # Apply the scope to the task name according to the rules for this kind
64
+ # of task. File based tasks ignore the scope when creating the name.
65
+ def scope_name(scope, task_name)
66
+ task_name
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def remote_package(*args, &block)
73
+ t = Rake::RemotePackage.define_task(*args, &block)
74
+ Rake::Task["remote_packages"].prerequisites.push(t.name).uniq!
75
+ t
76
+ end
77
+
78
+ desc "Downloads all remote packages"
79
+ task :remote_packages
@@ -0,0 +1,8 @@
1
+ def sudo(*command)
2
+ raise ArgumentError, "command missing" if command.empty?
3
+ if command.length == 1
4
+ sh "sudo #{command.first}"
5
+ else
6
+ sh "sudo", *command
7
+ end
8
+ end
@@ -0,0 +1,63 @@
1
+ require 'uri'
2
+
3
+ module Rake
4
+ class SvnDependency < Rake::Task
5
+ include Rake::DSL if defined? Rake::DSL
6
+
7
+ def branch(val = nil)
8
+ @branch = val if val
9
+ @branch || "trunk"
10
+ end
11
+
12
+ def needed?
13
+ return :checkout_missing unless File.directory?(local_path)
14
+ end
15
+
16
+ def timestamp
17
+ if File.exist?(local_path / 'svn')
18
+ File.mtime(local_path / 'svn')
19
+ else
20
+ Rake::EARLY
21
+ end
22
+ end
23
+
24
+ def local_path
25
+ RakePlus.cache_dir / "svn" / basename
26
+ end
27
+
28
+ def basename
29
+ base = File.basename(name)
30
+ end
31
+
32
+ def execute(args=nil)
33
+ if File.directory?(local_path)
34
+ Dir.chdir(local_path) do
35
+ sh "svn update"
36
+ end
37
+ else
38
+ FileUtils.mkdir_p File.dirname(local_path)
39
+ sh "svn checkout #{name}/#{branch} #{local_path}"
40
+ end
41
+ super
42
+ end
43
+
44
+ # Like .tar archives, creates a folder beneath it
45
+ def unpack_to(dir)
46
+ sh "cp -R #{local_path} #{dir}"
47
+ end
48
+
49
+ class << self
50
+ # Git based tasks ignore the scope when creating the name.
51
+ def scope_name(scope, task_name); task_name end
52
+ end
53
+ end
54
+ end
55
+
56
+ # Usage: git("git@github.com:zimbatm/direnv.git")
57
+ #
58
+
59
+
60
+ def svn(*args, &block)
61
+ Rake::SvnDependency.define_task(*args, &block)
62
+ end
63
+ alias svn_dep svn
@@ -0,0 +1,11 @@
1
+ require 'ostruct'
2
+ require 'erb'
3
+
4
+ def tpl(name, target, variables={})
5
+ ctx = OpenStruct.new(variables)
6
+ tpl_str = File.read(RakePlus.template_dir / "#{name}.erb")
7
+ tpl = ERB.new(tpl_str)
8
+ out = tpl.result ctx.send(:binding)
9
+ FileUtils.mkdir_p File.dirname(target)
10
+ File.open(target, 'w') do |f| f.write out end
11
+ end
@@ -0,0 +1,4 @@
1
+ # Tired of re-running your rake task with -t because it throwed an exception ?
2
+ #
3
+ # This will turn on the trace by default so that you don't have to do it again.
4
+ Rake.application.options.trace = true
data/lib/rake-plus.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'rake-plus/trace'
2
+ require 'rake-plus/path'
3
+
4
+ module RakePlus
5
+ @top = path(Dir.pwd)
6
+ @cache_dir = @top / '.cache'
7
+ @build_dir = @top / 'build'
8
+ @template_dir = @top / 'tpl'
9
+
10
+ class << self
11
+ attr_reader :top
12
+ attr_accessor :cache_dir
13
+ attr_accessor :build_dir
14
+ attr_accessor :template_dir
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rake-plus
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.5'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jonas Pfenniger
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: A collection of rake extensions that are useful all around
31
+ email: jonas@pfenniger.name
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - README.md
37
+ - lib/rake-plus/bdep.rb
38
+ - lib/rake-plus/git_dep.rb
39
+ - lib/rake-plus/gitver.rb
40
+ - lib/rake-plus/path.rb
41
+ - lib/rake-plus/rake_ext.rb
42
+ - lib/rake-plus/recipe.rb
43
+ - lib/rake-plus/remote_package.rb
44
+ - lib/rake-plus/sudo.rb
45
+ - lib/rake-plus/svn_dep.rb
46
+ - lib/rake-plus/tpl.rb
47
+ - lib/rake-plus/trace.rb
48
+ - lib/rake-plus.rb
49
+ homepage: https://github.com/zimbatm/rake-plus
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 1.8.19
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: A shiny rake extensions collection
73
+ test_files: []