gold 0.3.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/History.txt +16 -0
- data/README.txt +67 -0
- data/Rakefile +21 -0
- data/bin/gold +5 -0
- data/lib/gold.rb +282 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- metadata +96 -0
data/History.txt
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
== 0.3.1 2009-10-02
|
2
|
+
|
3
|
+
* 1 major enhancement:
|
4
|
+
* Storing 'settings' in the projects .git directory
|
5
|
+
* Added 'rebase' method.
|
6
|
+
* Made a gem
|
7
|
+
|
8
|
+
== 0.2.0 2009-09-08
|
9
|
+
|
10
|
+
* 1 major enhancement:
|
11
|
+
* Added 'add_dev' and 'setup' methods
|
12
|
+
|
13
|
+
== 0.1.0 2009-09-06
|
14
|
+
|
15
|
+
* 1 major enhancement:
|
16
|
+
* Initial alpha release
|
data/README.txt
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
== SUMMARY:
|
2
|
+
|
3
|
+
Gold is a simple executable that helps git teamwork.
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
This is the workflow we use when developing zena.
|
8
|
+
|
9
|
+
The main idea is that developers work on feature branches on their fork and send an
|
10
|
+
email to the reviewer when work is ready. The reviewer pulls from these branches,
|
11
|
+
checks that all is good and either apply the commits to the gold master or abort.
|
12
|
+
|
13
|
+
There is a script called 'gold' that helps use this workflow once the remote references
|
14
|
+
are added.
|
15
|
+
|
16
|
+
Any questions ? Ask zena's mailing list: http://zenadmin.org/en/community
|
17
|
+
|
18
|
+
~~
|
19
|
+
|
20
|
+
== Workflow:
|
21
|
+
|
22
|
+
You need to update the Settings in the 'gold' script to match your own project and emails.
|
23
|
+
|
24
|
+
Developer setup
|
25
|
+
---------------
|
26
|
+
|
27
|
+
1. login on github (John)
|
28
|
+
2. fork sandbox
|
29
|
+
3. on local machine, clone your own fork
|
30
|
+
> git clone git@github.com:john/PROJECT.git
|
31
|
+
> cd PROJECT
|
32
|
+
> gold setup
|
33
|
+
|
34
|
+
Working on new 'floppy' feature
|
35
|
+
-------------------------------
|
36
|
+
|
37
|
+
[John] (developer, on his own fork)
|
38
|
+
> git checkout gold
|
39
|
+
> git pull
|
40
|
+
> git checkout -b floppy
|
41
|
+
> commit, commit
|
42
|
+
# propose
|
43
|
+
> gold propose
|
44
|
+
|
45
|
+
[reviewer]
|
46
|
+
# only if john is not a remote yet
|
47
|
+
> gold add_dev john
|
48
|
+
# review
|
49
|
+
> gold review john/floppy
|
50
|
+
# fail
|
51
|
+
> gold fail
|
52
|
+
|
53
|
+
[John]
|
54
|
+
> reset, commit, squash, etc
|
55
|
+
# propose again
|
56
|
+
> gold propose
|
57
|
+
|
58
|
+
[reviewer]
|
59
|
+
# review
|
60
|
+
> gold review john/floppy
|
61
|
+
# ok
|
62
|
+
> gold ok
|
63
|
+
|
64
|
+
[John]
|
65
|
+
# cleanup
|
66
|
+
> git co floppy
|
67
|
+
> gold cleanup
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'bones'
|
3
|
+
Bones.setup
|
4
|
+
rescue LoadError
|
5
|
+
begin
|
6
|
+
load 'tasks/setup.rb'
|
7
|
+
rescue LoadError
|
8
|
+
raise RuntimeError, '### please install the "bones" gem ###'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
PROJ.name = 'gold'
|
13
|
+
PROJ.summary = 'Gold is a simple executable that helps git teamwork.'
|
14
|
+
PROJ.authors = 'Gaspard Bucher'
|
15
|
+
PROJ.email = 'gaspard@teti.ch'
|
16
|
+
PROJ.url = 'http://zenadmin.org/574'
|
17
|
+
PROJ.version = '0.3.1'
|
18
|
+
PROJ.rubyforge.name = 'gold'
|
19
|
+
|
20
|
+
PROJ.spec.opts << '--color'
|
21
|
+
PROJ.gem.files = ['History.txt', 'README.txt', 'bin/gold', 'Rakefile', 'lib/gold.rb']
|
data/bin/gold
ADDED
data/lib/gold.rb
ADDED
@@ -0,0 +1,282 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
# Sending mail not working on mac os x ?
|
6
|
+
# http://www.macosxhints.com/article.php?story=20081217161612647
|
7
|
+
|
8
|
+
# install
|
9
|
+
# > sudo cp gold /usr/local/bin/
|
10
|
+
# > chmod a+x /usr/local/bin/gold
|
11
|
+
|
12
|
+
# TODO: move Settings inside each project ?
|
13
|
+
|
14
|
+
class Gold
|
15
|
+
|
16
|
+
DefaultSettings = {
|
17
|
+
'gold_branch' => 'gold', # name of local branch tracking gold master (in developer's local git)
|
18
|
+
'gold_remote' => 'zena', # name of remote 'gold' reference project
|
19
|
+
'gold_repository' => 'git://github.com/zena/zena.git',
|
20
|
+
'developer_name' => 'john', # developer's name (same as github account)
|
21
|
+
'developer_email' => 'developer@example.com', # developer's email
|
22
|
+
'reviewer' => 'reviewer@example.com' # reviewer's email
|
23
|
+
}
|
24
|
+
|
25
|
+
SettingsMessages = {
|
26
|
+
'1. gold_branch' => 'name of local branch that will track gold master',
|
27
|
+
'2. gold_remote' => 'name of remote gold master',
|
28
|
+
'3. gold_repository' => 'remote repository',
|
29
|
+
'4. developer_name' => 'developer\'s account on remote repository',
|
30
|
+
'5. developer_email' => 'developer\'s email',
|
31
|
+
'6. reviewer' => 'reviewer\'s email'
|
32
|
+
}
|
33
|
+
|
34
|
+
DefaultSettings.keys.each do |k|
|
35
|
+
define_method(k) do
|
36
|
+
settings[k]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.run(args)
|
41
|
+
self.new.run(args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def run(args)
|
45
|
+
case args[0]
|
46
|
+
when 'add_dev'
|
47
|
+
add_dev args[1], args[2]
|
48
|
+
when 'check', 'review'
|
49
|
+
review args[1]
|
50
|
+
when 'propose'
|
51
|
+
propose args[1]
|
52
|
+
when 'settings'
|
53
|
+
create_settings_file
|
54
|
+
else
|
55
|
+
if args[0] && self.respond_to?(args[0])
|
56
|
+
send(args[0])
|
57
|
+
else
|
58
|
+
show_usage
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def setup
|
64
|
+
return error("Could not create settings file #{settings_path.inspect}.") unless create_settings_file
|
65
|
+
return error("Could create remote #{gold_remote}.") unless system("git remote add #{gold_remote} #{gold_repository}")
|
66
|
+
return error("Could not fetch #{gold_remote}.") unless system("git fetch #{gold_remote}")
|
67
|
+
return error("Could not create #{gold_branch} branch.") unless system("git checkout --track -b #{gold_branch} #{gold_remote}/master")
|
68
|
+
end
|
69
|
+
|
70
|
+
def rebase
|
71
|
+
branch = current_branch
|
72
|
+
return error("Could not find current branch.") unless branch
|
73
|
+
return error("You cannot rebase the #{gold_branch} branch !") if branch == gold_branch
|
74
|
+
return error("You cannot rebase the master branch !") if branch == 'master'
|
75
|
+
return error("Could not checkout #{gold_branch}.") unless system("git checkout #{gold_branch}")
|
76
|
+
return error("Could not pull #{gold_branch}.") unless system("git pull")
|
77
|
+
return error("Could not checkout #{branch}.") unless system("git checkout #{branch}")
|
78
|
+
return error("Could not rebase --interactive #{gold_branch}.") unless system("git rebase --interactive #{gold_branch}")
|
79
|
+
end
|
80
|
+
|
81
|
+
def propose(msg)
|
82
|
+
branch = current_branch
|
83
|
+
return error("Could not find current branch.") unless branch
|
84
|
+
return error("You cannot propose the #{gold_branch} branch !") if branch == gold_branch
|
85
|
+
return error("You cannot propose the master branch !") if branch == 'master'
|
86
|
+
return error("Could not checkout #{gold_branch}.") unless system("git checkout #{gold_branch}")
|
87
|
+
return error("Could not pull #{gold_branch}.") unless system("git pull")
|
88
|
+
return error("Could not checkout #{branch}.") unless system("git checkout #{branch}")
|
89
|
+
return error("Could not rebase #{branch} with #{gold_branch}.") unless system("git rebase #{gold_branch}")
|
90
|
+
return error("Could not push #{branch} to origin.") unless system("git push origin +#{branch}")
|
91
|
+
subject = "#{gold_remote}: review #{developer_name}/#{branch}"
|
92
|
+
tmpf = Tempfile.new('gold_msg')
|
93
|
+
tmpf.write %Q{From:#{developer_email}
|
94
|
+
Subject:#{subject}
|
95
|
+
|
96
|
+
============
|
97
|
+
gold review #{developer_name}/#{branch}
|
98
|
+
============
|
99
|
+
|
100
|
+
#{msg}
|
101
|
+
}
|
102
|
+
tmpf.close
|
103
|
+
return error("Could not send email to #{reviewer}.") unless system("sendmail -F '#{developer_name}' #{reviewer} < #{tmpf.path}")
|
104
|
+
tmpf.delete
|
105
|
+
puts "Mail sent to #{reviewer}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def add_dev(name, repo = nil)
|
109
|
+
return error("Missing 'developer name' parameter.") unless name
|
110
|
+
repo ||= "#{gold_repository.split('/')[0..2].join('/')}/#{name}/#{gold_remote}.git"
|
111
|
+
return error("Could add developer #{name} (#{repo}).") unless system("git remote add #{name} #{repo}")
|
112
|
+
return error("Could not fetch #{name}.") unless system("git fetch #{name}")
|
113
|
+
end
|
114
|
+
|
115
|
+
def review(remote_branch)
|
116
|
+
return error("Missing 'remote_branch' parameter.") unless remote_branch
|
117
|
+
return error("'remote_branch' format should be remote/branch.") unless remote_branch =~ /^(.+)\/(.+)$/
|
118
|
+
remote, branch = $1, $2
|
119
|
+
return error("Could not checkout master.") unless system("git co master")
|
120
|
+
return error("Could not checkout #{remote}_#{branch}.") unless system("git co -b #{remote}_#{branch}")
|
121
|
+
return error("Could not pull #{remote_branch}.") unless system("git pull #{remote} #{branch}")
|
122
|
+
return error("Could not rebase.") unless system("git rebase master")
|
123
|
+
system("git diff master | $EDITOR")
|
124
|
+
true
|
125
|
+
end
|
126
|
+
|
127
|
+
def ok
|
128
|
+
branch = current_branch
|
129
|
+
return error("Could not find current branch.") unless branch
|
130
|
+
return error("Could not rebase.") unless system("git rebase master")
|
131
|
+
return error("Could not checkout master.") unless system("git co master")
|
132
|
+
return error("Could not fast-forward merge #{branch} into master.") unless system("git merge --ff #{branch}")
|
133
|
+
return error("Could not delete #{branch}.") unless system("git branch -d #{branch}")
|
134
|
+
puts "-------------------\n\nSuccessfully applied #{branch} to golden master !\n\nPlease git push when you are ready."
|
135
|
+
end
|
136
|
+
|
137
|
+
def fail
|
138
|
+
branch = current_branch
|
139
|
+
return error("Could not find current branch.") unless branch
|
140
|
+
return error("You cannot 'fail' master !") unless branch != 'master'
|
141
|
+
return error("Could not checkout master.") unless system("git co master")
|
142
|
+
return nil unless remove(branch, true)
|
143
|
+
end
|
144
|
+
|
145
|
+
def cleanup
|
146
|
+
branch = current_branch
|
147
|
+
return error("Could not find current branch.") unless branch
|
148
|
+
return error("You cannot 'cleanup' master or #{gold_branch} !") if branch == 'master' || branch == gold_branch
|
149
|
+
return error("Could not checkout #{gold_branch}.") unless system("git co #{gold_branch}")
|
150
|
+
return error("Could not pull #{gold_remote}.") unless system("git pull #{gold_remote}")
|
151
|
+
return error("Could not checkout #{branch}.") unless system("git co #{branch}")
|
152
|
+
return error("Could rebase on top of #{gold_branch}.") unless system("git rebase #{gold_branch}")
|
153
|
+
return error("Could not checkout #{gold_branch}.") unless system("git co #{gold_branch}")
|
154
|
+
return nil unless remove(branch)
|
155
|
+
return error("Could not clear remote branch.") unless system("git push origin :#{branch}")
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def error(msg)
|
162
|
+
puts msg
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
|
166
|
+
def current_branch
|
167
|
+
branch = nil
|
168
|
+
`git branch`.split("\n").each do |b|
|
169
|
+
if b =~ /^\*\s+(.+)$/
|
170
|
+
branch = $1
|
171
|
+
break
|
172
|
+
end
|
173
|
+
end
|
174
|
+
branch
|
175
|
+
end
|
176
|
+
|
177
|
+
def remove(branch, force=false)
|
178
|
+
print "Remove branch #{branch} ? (y,N) "
|
179
|
+
yn = STDIN.gets
|
180
|
+
if yn.downcase.strip == 'y'
|
181
|
+
return error("Could not delete #{branch}.") unless system("git branch -#{force ? 'D' : 'd'} #{branch}")
|
182
|
+
true
|
183
|
+
else
|
184
|
+
return error("Could not checkout #{branch}.") unless system("git co #{branch}")
|
185
|
+
false
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def create_settings_file
|
190
|
+
FileUtils::mkpath(File.dirname(settings_path)) unless File.exist?(File.dirname(settings_path))
|
191
|
+
defaults = settings
|
192
|
+
new_settings = {}
|
193
|
+
SettingsMessages.keys.sort.each do |key|
|
194
|
+
real_key = key.gsub(/\A.*? /,'')
|
195
|
+
print "#{SettingsMessages[key]} (#{defaults[real_key]}): "
|
196
|
+
value = STDIN.gets.chomp
|
197
|
+
new_settings[real_key] = value == '' ? DefaultSettings[real_key] : value
|
198
|
+
end
|
199
|
+
File.open(settings_path, 'wb') do |f|
|
200
|
+
f.puts YAML::dump(new_settings)
|
201
|
+
end
|
202
|
+
puts "New settings written to '#{settings_path}'"
|
203
|
+
true
|
204
|
+
end
|
205
|
+
|
206
|
+
def settings
|
207
|
+
@settings ||= begin
|
208
|
+
if File.exist?(settings_path)
|
209
|
+
YAML::load(File.read(settings_path))
|
210
|
+
else
|
211
|
+
DefaultSettings
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def settings_path
|
217
|
+
@settings_path ||= begin
|
218
|
+
path = File.expand_path('.').split('/')
|
219
|
+
while path != []
|
220
|
+
if File.exist?(File.join(path + ['.git']))
|
221
|
+
break
|
222
|
+
else
|
223
|
+
path.pop
|
224
|
+
end
|
225
|
+
end
|
226
|
+
(path + ['.git', 'gold.yml']).flatten.join('/')
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def show_usage
|
231
|
+
error(%Q{
|
232
|
+
Usage:
|
233
|
+
|
234
|
+
Reviewer
|
235
|
+
========
|
236
|
+
|
237
|
+
Add a new developer reference
|
238
|
+
-----------------------------
|
239
|
+
> gold add_dev john
|
240
|
+
or
|
241
|
+
> gold add_dev john git://github.com/john/foo.git
|
242
|
+
|
243
|
+
Checkout a remote branch and view diff
|
244
|
+
--------------------------------------
|
245
|
+
> gold check john/floppy
|
246
|
+
|
247
|
+
Include commits into master branch
|
248
|
+
----------------------------------
|
249
|
+
(on the new feature branch created by previous 'review')
|
250
|
+
> gold ok
|
251
|
+
|
252
|
+
The code is not ready yet, cleanup
|
253
|
+
----------------------------------
|
254
|
+
(on the new feature branch created by previous 'review')
|
255
|
+
> gold fail
|
256
|
+
|
257
|
+
Developer
|
258
|
+
=========
|
259
|
+
|
260
|
+
Setup
|
261
|
+
-----
|
262
|
+
> gold setup
|
263
|
+
> gold settings
|
264
|
+
|
265
|
+
Rebase on top of latest gold
|
266
|
+
----------------------------
|
267
|
+
(on the new feature branch)
|
268
|
+
> gold rebase
|
269
|
+
|
270
|
+
Propose branch
|
271
|
+
--------------
|
272
|
+
(on the new feature branch)
|
273
|
+
> gold propose
|
274
|
+
|
275
|
+
Cleanup after commit acceptation
|
276
|
+
--------------------------------
|
277
|
+
(on the new feature branch)
|
278
|
+
> gold cleanup
|
279
|
+
|
280
|
+
})
|
281
|
+
end
|
282
|
+
end
|
data/tasks/ann.rake
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
begin
|
3
|
+
require 'bones/smtp_tls'
|
4
|
+
rescue LoadError
|
5
|
+
require 'net/smtp'
|
6
|
+
end
|
7
|
+
require 'time'
|
8
|
+
|
9
|
+
namespace :ann do
|
10
|
+
|
11
|
+
# A prerequisites task that all other tasks depend upon
|
12
|
+
task :prereqs
|
13
|
+
|
14
|
+
file PROJ.ann.file do
|
15
|
+
ann = PROJ.ann
|
16
|
+
puts "Generating #{ann.file}"
|
17
|
+
File.open(ann.file,'w') do |fd|
|
18
|
+
fd.puts("#{PROJ.name} version #{PROJ.version}")
|
19
|
+
fd.puts(" by #{Array(PROJ.authors).first}") if PROJ.authors
|
20
|
+
fd.puts(" #{PROJ.url}") if PROJ.url.valid?
|
21
|
+
fd.puts(" (the \"#{PROJ.release_name}\" release)") if PROJ.release_name
|
22
|
+
fd.puts
|
23
|
+
fd.puts("== DESCRIPTION")
|
24
|
+
fd.puts
|
25
|
+
fd.puts(PROJ.description)
|
26
|
+
fd.puts
|
27
|
+
fd.puts(PROJ.changes.sub(%r/^.*$/, '== CHANGES'))
|
28
|
+
fd.puts
|
29
|
+
ann.paragraphs.each do |p|
|
30
|
+
fd.puts "== #{p.upcase}"
|
31
|
+
fd.puts
|
32
|
+
fd.puts paragraphs_of(PROJ.readme_file, p).join("\n\n")
|
33
|
+
fd.puts
|
34
|
+
end
|
35
|
+
fd.puts ann.text if ann.text
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Create an announcement file"
|
40
|
+
task :announcement => ['ann:prereqs', PROJ.ann.file]
|
41
|
+
|
42
|
+
desc "Send an email announcement"
|
43
|
+
task :email => ['ann:prereqs', PROJ.ann.file] do
|
44
|
+
ann = PROJ.ann
|
45
|
+
from = ann.email[:from] || Array(PROJ.authors).first || PROJ.email
|
46
|
+
to = Array(ann.email[:to])
|
47
|
+
|
48
|
+
### build a mail header for RFC 822
|
49
|
+
rfc822msg = "From: #{from}\n"
|
50
|
+
rfc822msg << "To: #{to.join(',')}\n"
|
51
|
+
rfc822msg << "Subject: [ANN] #{PROJ.name} #{PROJ.version}"
|
52
|
+
rfc822msg << " (#{PROJ.release_name})" if PROJ.release_name
|
53
|
+
rfc822msg << "\n"
|
54
|
+
rfc822msg << "Date: #{Time.new.rfc822}\n"
|
55
|
+
rfc822msg << "Message-Id: "
|
56
|
+
rfc822msg << "<#{"%.8f" % Time.now.to_f}@#{ann.email[:domain]}>\n\n"
|
57
|
+
rfc822msg << File.read(ann.file)
|
58
|
+
|
59
|
+
params = [:server, :port, :domain, :acct, :passwd, :authtype].map do |key|
|
60
|
+
ann.email[key]
|
61
|
+
end
|
62
|
+
|
63
|
+
params[3] = PROJ.email if params[3].nil?
|
64
|
+
|
65
|
+
if params[4].nil?
|
66
|
+
STDOUT.write "Please enter your e-mail password (#{params[3]}): "
|
67
|
+
params[4] = STDIN.gets.chomp
|
68
|
+
end
|
69
|
+
|
70
|
+
### send email
|
71
|
+
Net::SMTP.start(*params) {|smtp| smtp.sendmail(rfc822msg, from, to)}
|
72
|
+
end
|
73
|
+
end # namespace :ann
|
74
|
+
|
75
|
+
desc 'Alias to ann:announcement'
|
76
|
+
task :ann => 'ann:announcement'
|
77
|
+
|
78
|
+
CLOBBER << PROJ.ann.file
|
79
|
+
|
80
|
+
# EOF
|
data/tasks/bones.rake
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
if HAVE_BONES
|
3
|
+
|
4
|
+
namespace :bones do
|
5
|
+
|
6
|
+
desc 'Show the PROJ open struct'
|
7
|
+
task :debug do |t|
|
8
|
+
atr = if t.application.top_level_tasks.length == 2
|
9
|
+
t.application.top_level_tasks.pop
|
10
|
+
end
|
11
|
+
|
12
|
+
if atr then Bones::Debug.show_attr(PROJ, atr)
|
13
|
+
else Bones::Debug.show PROJ end
|
14
|
+
end
|
15
|
+
|
16
|
+
end # namespace :bones
|
17
|
+
|
18
|
+
end # HAVE_BONES
|
19
|
+
|
20
|
+
# EOF
|
data/tasks/gem.rake
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
|
2
|
+
require 'find'
|
3
|
+
require 'rake/packagetask'
|
4
|
+
require 'rubygems/user_interaction'
|
5
|
+
require 'rubygems/builder'
|
6
|
+
|
7
|
+
module Bones
|
8
|
+
class GemPackageTask < Rake::PackageTask
|
9
|
+
# Ruby GEM spec containing the metadata for this package. The
|
10
|
+
# name, version and package_files are automatically determined
|
11
|
+
# from the GEM spec and don't need to be explicitly provided.
|
12
|
+
#
|
13
|
+
attr_accessor :gem_spec
|
14
|
+
|
15
|
+
# Tasks from the Bones gem directory
|
16
|
+
attr_reader :bones_files
|
17
|
+
|
18
|
+
# Create a GEM Package task library. Automatically define the gem
|
19
|
+
# if a block is given. If no block is supplied, then +define+
|
20
|
+
# needs to be called to define the task.
|
21
|
+
#
|
22
|
+
def initialize(gem_spec)
|
23
|
+
init(gem_spec)
|
24
|
+
yield self if block_given?
|
25
|
+
define if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Initialization tasks without the "yield self" or define
|
29
|
+
# operations.
|
30
|
+
#
|
31
|
+
def init(gem)
|
32
|
+
super(gem.name, gem.version)
|
33
|
+
@gem_spec = gem
|
34
|
+
@package_files += gem_spec.files if gem_spec.files
|
35
|
+
@bones_files = []
|
36
|
+
|
37
|
+
local_setup = File.join(Dir.pwd, %w[tasks setup.rb])
|
38
|
+
if !test(?e, local_setup)
|
39
|
+
Dir.glob(::Bones.path(%w[lib bones tasks *])).each {|fn| bones_files << fn}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create the Rake tasks and actions specified by this
|
44
|
+
# GemPackageTask. (+define+ is automatically called if a block is
|
45
|
+
# given to +new+).
|
46
|
+
#
|
47
|
+
def define
|
48
|
+
super
|
49
|
+
task :prereqs
|
50
|
+
task :package => ['gem:prereqs', "#{package_dir_path}/#{gem_file}"]
|
51
|
+
file "#{package_dir_path}/#{gem_file}" => [package_dir_path] + package_files + bones_files do
|
52
|
+
when_writing("Creating GEM") {
|
53
|
+
chdir(package_dir_path) do
|
54
|
+
Gem::Builder.new(gem_spec).build
|
55
|
+
verbose(true) {
|
56
|
+
mv gem_file, "../#{gem_file}"
|
57
|
+
}
|
58
|
+
end
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
file package_dir_path => bones_files do
|
63
|
+
mkdir_p package_dir rescue nil
|
64
|
+
|
65
|
+
gem_spec.files = (gem_spec.files +
|
66
|
+
bones_files.map {|fn| File.join('tasks', File.basename(fn))}).sort
|
67
|
+
|
68
|
+
bones_files.each do |fn|
|
69
|
+
base_fn = File.join('tasks', File.basename(fn))
|
70
|
+
f = File.join(package_dir_path, base_fn)
|
71
|
+
fdir = File.dirname(f)
|
72
|
+
mkdir_p(fdir) if !File.exist?(fdir)
|
73
|
+
if File.directory?(fn)
|
74
|
+
mkdir_p(f)
|
75
|
+
else
|
76
|
+
raise "file name conflict for '#{base_fn}' (conflicts with '#{fn}')" if test(?e, f)
|
77
|
+
safe_ln(fn, f)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def gem_file
|
84
|
+
if @gem_spec.platform == Gem::Platform::RUBY
|
85
|
+
"#{package_name}.gem"
|
86
|
+
else
|
87
|
+
"#{package_name}-#{@gem_spec.platform}.gem"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end # class GemPackageTask
|
91
|
+
end # module Bones
|
92
|
+
|
93
|
+
namespace :gem do
|
94
|
+
|
95
|
+
PROJ.gem._spec = Gem::Specification.new do |s|
|
96
|
+
s.name = PROJ.name
|
97
|
+
s.version = PROJ.version
|
98
|
+
s.summary = PROJ.summary
|
99
|
+
s.authors = Array(PROJ.authors)
|
100
|
+
s.email = PROJ.email
|
101
|
+
s.homepage = Array(PROJ.url).first
|
102
|
+
s.rubyforge_project = PROJ.rubyforge.name
|
103
|
+
|
104
|
+
s.description = PROJ.description
|
105
|
+
|
106
|
+
PROJ.gem.dependencies.each do |dep|
|
107
|
+
s.add_dependency(*dep)
|
108
|
+
end
|
109
|
+
|
110
|
+
PROJ.gem.development_dependencies.each do |dep|
|
111
|
+
s.add_development_dependency(*dep)
|
112
|
+
end
|
113
|
+
|
114
|
+
s.files = PROJ.gem.files
|
115
|
+
s.executables = PROJ.gem.executables.map {|fn| File.basename(fn)}
|
116
|
+
s.extensions = PROJ.gem.files.grep %r/extconf\.rb$/
|
117
|
+
|
118
|
+
s.bindir = 'bin'
|
119
|
+
dirs = Dir["{#{PROJ.libs.join(',')}}"]
|
120
|
+
s.require_paths = dirs unless dirs.empty?
|
121
|
+
|
122
|
+
incl = Regexp.new(PROJ.rdoc.include.join('|'))
|
123
|
+
excl = PROJ.rdoc.exclude.dup.concat %w[\.rb$ ^(\.\/|\/)?ext]
|
124
|
+
excl = Regexp.new(excl.join('|'))
|
125
|
+
rdoc_files = PROJ.gem.files.find_all do |fn|
|
126
|
+
case fn
|
127
|
+
when excl; false
|
128
|
+
when incl; true
|
129
|
+
else false end
|
130
|
+
end
|
131
|
+
s.rdoc_options = PROJ.rdoc.opts + ['--main', PROJ.rdoc.main]
|
132
|
+
s.extra_rdoc_files = rdoc_files
|
133
|
+
s.has_rdoc = true
|
134
|
+
|
135
|
+
if test ?f, PROJ.test.file
|
136
|
+
s.test_file = PROJ.test.file
|
137
|
+
else
|
138
|
+
s.test_files = PROJ.test.files.to_a
|
139
|
+
end
|
140
|
+
|
141
|
+
# Do any extra stuff the user wants
|
142
|
+
PROJ.gem.extras.each do |msg, val|
|
143
|
+
case val
|
144
|
+
when Proc
|
145
|
+
val.call(s.send(msg))
|
146
|
+
else
|
147
|
+
s.send "#{msg}=", val
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end # Gem::Specification.new
|
151
|
+
|
152
|
+
Bones::GemPackageTask.new(PROJ.gem._spec) do |pkg|
|
153
|
+
pkg.need_tar = PROJ.gem.need_tar
|
154
|
+
pkg.need_zip = PROJ.gem.need_zip
|
155
|
+
end
|
156
|
+
|
157
|
+
desc 'Show information about the gem'
|
158
|
+
task :debug => 'gem:prereqs' do
|
159
|
+
puts PROJ.gem._spec.to_ruby
|
160
|
+
end
|
161
|
+
|
162
|
+
desc 'Write the gemspec '
|
163
|
+
task :spec => 'gem:prereqs' do
|
164
|
+
File.open("#{PROJ.name}.gemspec", 'w') do |f|
|
165
|
+
f.write PROJ.gem._spec.to_ruby
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
desc 'Install the gem'
|
170
|
+
task :install => [:clobber, 'gem:package'] do
|
171
|
+
sh "#{SUDO} #{GEM} install --local pkg/#{PROJ.gem._spec.full_name}"
|
172
|
+
|
173
|
+
# use this version of the command for rubygems > 1.0.0
|
174
|
+
#sh "#{SUDO} #{GEM} install --no-update-sources pkg/#{PROJ.gem._spec.full_name}"
|
175
|
+
end
|
176
|
+
|
177
|
+
desc 'Uninstall the gem'
|
178
|
+
task :uninstall do
|
179
|
+
installed_list = Gem.source_index.find_name(PROJ.name)
|
180
|
+
if installed_list and installed_list.collect { |s| s.version.to_s}.include?(PROJ.version) then
|
181
|
+
sh "#{SUDO} #{GEM} uninstall --version '#{PROJ.version}' --ignore-dependencies --executables #{PROJ.name}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
desc 'Reinstall the gem'
|
186
|
+
task :reinstall => [:uninstall, :install]
|
187
|
+
|
188
|
+
desc 'Cleanup the gem'
|
189
|
+
task :cleanup do
|
190
|
+
sh "#{SUDO} #{GEM} cleanup #{PROJ.gem._spec.name}"
|
191
|
+
end
|
192
|
+
end # namespace :gem
|
193
|
+
|
194
|
+
|
195
|
+
desc 'Alias to gem:package'
|
196
|
+
task :gem => 'gem:package'
|
197
|
+
|
198
|
+
task :clobber => 'gem:clobber_package'
|
199
|
+
remove_desc_for_task 'gem:clobber_package'
|
200
|
+
|
201
|
+
# EOF
|