-
Notifications
You must be signed in to change notification settings - Fork 0
/
Rakefile
220 lines (195 loc) · 6.98 KB
/
Rakefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# encoding: UTF-8
# -*- ruby -*-
require 'erb'
require 'rake/testtask'
require 'rubygems/package_task'
require 'rake/clean'
require 'yard'
# Load the gemspec file for this project.
GEMSPEC = Dir['*.gemspec'].first
SPEC = eval(File.read(GEMSPEC), nil, GEMSPEC)
# A dynamically generated list of files that should match the manifest (the
# combined contents of SPEC.files and SPEC.test_files). The idea is for this
# list to contain all project files except for those that have been explicitly
# excluded. This list will be compared with the manifest from the SPEC in order
# to help catch the addition or removal of files to or from the project that
# have not been accounted for either by an exclusion here or an inclusion in the
# SPEC manifest.
#
# NOTE:
# It is critical that the manifest is *not* automatically generated via globbing
# and the like; otherwise, this will yield a simple comparison between
# redundantly generated lists of files that probably will not protect the
# project from the unintentional inclusion or exclusion of files in the
# distribution.
PKG_FILES = FileList.new(Dir.glob('**/*', File::FNM_DOTMATCH)) do |files|
# Exclude anything that doesn't exist as well as directories.
files.exclude {|file| ! File.exist?(file) || File.directory?(file)}
# Exclude Git administrative files.
files.exclude(%r{(^|[/\\])\.git(ignore|modules|keep)?([/\\]|$)})
# Exclude editor swap/temporary files.
files.exclude('**/.*.sw?')
# Exclude the gemspec file.
files.exclude(GEMSPEC)
# Exclude the README template file.
files.exclude('README.md.erb')
# Exclude resources for bundler.
files.exclude('Gemfile', 'Gemfile.lock')
files.exclude(%r{^.bundle([/\\]|$)})
files.exclude(%r{^vendor/bundle([/\\]|$)})
# Exclude generated content, except for the README file.
files.exclude(%r{^(pkg|doc|.yardoc)([/\\]|$)})
# Exclude Rubinius compiled Ruby files.
files.exclude('**/*.rbc')
end
# Make sure that :clean and :clobber will not whack the repository files.
CLEAN.exclude('.git/**')
# Vim swap files are fair game for clean up.
CLEAN.include('**/.*.sw?')
# Returns the value of the VERSION environment variable as a Gem::Version object
# assuming it is set and a valid Gem version string. Otherwise, raises an
# exception.
def get_version_argument
version = ENV['VERSION']
if version.to_s.empty?
raise "No version specified: Add VERSION=X.Y.Z to the command line"
end
begin
Gem::Version.create(version.dup)
rescue ArgumentError
raise "Invalid version specified in `VERSION=#{version}'"
end
end
# Performs an in place, per line edit of the file indicated by _path_ by calling
# the sub method on each line and passing _pattern_, _replacement_, and _b_ as
# arguments.
def file_sub(path, pattern, replacement = nil, &b)
tmp_path = "#{path}.tmp"
File.open(path) do |infile|
File.open(tmp_path, 'w') do |outfile|
infile.each do |line|
outfile.write(line.sub(pattern, replacement, &b))
end
end
end
File.rename(tmp_path, path)
end
# Updates the version string in the gemspec file to the string in _version_.
def set_version(version)
file_sub(GEMSPEC, /(\.version\s*=\s*).*/, "\\1'#{version}'")
end
# Returns a string that is line wrapped at word boundaries, where each line is
# no longer than _line_width_ characters.
#
# This is mostly lifted directly from ActionView::Helpers::TextHelper.
def word_wrap(text, line_width = 80)
text.split("\n").collect do |line|
line.length > line_width ?
line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip :
line
end * "\n"
end
desc 'Alias for build:gem'
task :build => 'build:gem'
# Build related tasks.
namespace :build do
# Create the gem and package tasks.
Gem::PackageTask.new(SPEC).define
# Ensure that the manifest is consulted when building the gem. Any
# generated/compiled files should be available at that time.
task :gem => :check_manifest
desc 'Verify the manifest'
task :check_manifest do
manifest_files = (SPEC.files + SPEC.test_files).sort.uniq
pkg_files = PKG_FILES.sort.uniq
if manifest_files != pkg_files then
common_files = manifest_files & pkg_files
manifest_files -= common_files
pkg_files -= common_files
message = ["The manifest does not match the automatic file list."]
unless manifest_files.empty? then
message << " Extraneous files:\n " + manifest_files.join("\n ")
end
unless pkg_files.empty?
message << " Missing files:\n " + pkg_files.join("\n ")
end
raise message.join("\n")
end
end
# Creates the README.md file from its template and other sources.
file 'README.md' => ['README.md.erb', 'LICENSE', GEMSPEC] do
spec = SPEC
File.open('README.md', 'w') do |readme|
readme.write(
ERB.new(File.read('README.md.erb'), nil, '-').result(binding)
)
end
end
end
# Ensure that the clobber task also clobbers package files.
task :clobber => 'build:clobber_package'
# Create the documentation task.
YARD::Rake::YardocTask.new
# Ensure that the README file is (re)generated first.
task :yard => 'README.md'
# Gem related tasks.
namespace :gem do
desc 'Alias for build:gem'
task :build => 'build:gem'
desc 'Publish the gemfile'
task :publish => ['version:check', :test, 'repo:tag', :build] do
sh "gem push pkg/#{SPEC.name}-#{SPEC.version}*.gem"
end
end
Rake::TestTask.new do |t|
t.pattern = 'spec/**/*_spec.rb'
end
# Version string management tasks.
namespace :version do
desc 'Set the version for the project to a specified version'
task :set do
set_version(get_version_argument)
end
desc 'Set the version for the project back to 0.0.0'
task :reset do
set_version('0.0.0')
end
desc 'Check that all version strings are correctly set'
task :check => ['version:check:spec', 'version:check:news']
namespace :check do
desc 'Check that the version in the gemspec is correctly set'
task :spec do
version = get_version_argument
if version != SPEC.version
raise "The given version `#{version}' does not match the gemspec version `#{SPEC.version}'"
end
end
desc 'Check that the NEWS.md file mentions the version'
task :news do
version = get_version_argument
begin
File.open('NEWS.md') do |news|
unless news.each_line.any? {|l| l =~ /^## v#{Regexp.escape(version.to_s)} /}
raise "The NEWS.md file does not mention version `#{version}'"
end
end
rescue Errno::ENOENT
raise 'No NEWS.md file found'
end
end
end
end
# Repository and workspace management tasks.
namespace :repo do
desc 'Tag the current HEAD with the version string'
task :tag => :check_workspace do
version = get_version_argument
sh "git tag -s -m 'Release v#{version}' v#{version}"
end
desc 'Ensure the workspace is fully committed and clean'
task :check_workspace => ['README.md'] do
unless `git status --untracked-files=all --porcelain`.empty?
raise 'Workspace has been modified. Commit pending changes and try again.'
end
end
end