rake-deveiate 0.16.0 → 0.16.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6294dfb27450c830428cd59e747a2039b33691e2092a2a70470370612347297f
4
- data.tar.gz: fba0f6a23e4cd52de453bfb7d1441b873afe3a340cd0f137edf0621aeece57a4
3
+ metadata.gz: bbd17de1c4ec17eea0db8875fc492b5d0805a0d82aa7de27b23e2b01b2c7a4ff
4
+ data.tar.gz: f0c875ad3e5210dbef77df28ed6250e9c48cd44a0a9919e0155b94073244a9d8
5
5
  SHA512:
6
- metadata.gz: 23eea2fb28c372d70bd5456ce7de3fd474e9c5312db520ce9562af3fb35ee377514030acb6728bc3ae6e9376e7ac7fd066c1ea5cec868768102faa64ec5e8710
7
- data.tar.gz: 5806ea7d4bc077138b875e64ef4d11ad02d816a81d8e17760ffa4393451d08c2e8b70b3cfa354147dff88f4b20063ff76f673e11749d8fa9c4f2422ca39213e0
6
+ metadata.gz: 796a4604fbd50022c1cb5524de7e04615b4efcd9b15d9527cd0373b02efd77539e2b21aae7ae14edf0ed861c6b5c1d130f363359cdff99b9e7d37bfa4beb1671
7
+ data.tar.gz: b6c7300e3a40aaa5fa8d890621f88d59b1a1f51ae7257eb803dd5e6573d3d56d626cbb87f5ee1d0818b667c4d16be7434752eaf8118a0bbb33c298b65717f7da
Binary file
data.tar.gz.sig CHANGED
Binary file
data/History.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Release History for rake-deveiate
2
2
 
3
3
  ---
4
+ ## v0.16.1 [2020-11-23] Michael Granger <ged@FaerieMUD.org>
5
+
6
+ Bugfixes:
7
+
8
+ - Include missing `git` tasklib.
9
+
4
10
 
5
11
  ## v0.16.0 [2020-11-23] Michael Granger <ged@FaerieMUD.org>
6
12
 
@@ -35,7 +35,7 @@ class Rake::DevEiate < Rake::TaskLib
35
35
  VERSION_PATTERN = /VERSION\s*=\s*(?<quote>['"])(?<version>\d+(\.\d+){2}.*)\k<quote>/
36
36
 
37
37
  # The version of this library
38
- VERSION = '0.16.0'
38
+ VERSION = '0.16.1'
39
39
 
40
40
  # The server to release to by default
41
41
  DEFAULT_GEMSERVER = 'https://rubygems.org/'
@@ -0,0 +1,524 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'tempfile'
5
+ require 'shellwords'
6
+ require 'git'
7
+ require 'tty/editor'
8
+
9
+ require 'rake/deveiate' unless defined?( Rake::DevEiate )
10
+ require 'rake/deveiate/git-refinements'
11
+
12
+ using Rake::DevEiate::GitRefinements
13
+
14
+
15
+ # Git version-control tasks
16
+ module Rake::DevEiate::Git
17
+
18
+ # The name of the file to edit for the commit message
19
+ COMMIT_MSG_FILE = Pathname( 'commit-msg.txt' )
20
+
21
+ # The name of the ignore file
22
+ IGNORE_FILE = Rake::DevEiate::PROJECT_DIR + '.gitignore'
23
+
24
+ # Colors for presenting file statuses
25
+ STATUS_COLORS = {
26
+ 'M' => [:blue], # modified
27
+ 'A' => [:bold, :green], # added
28
+ 'R' => [:bold, :black], # removed
29
+ 'C' => [:white], # clean
30
+ '!' => [:bold, :white, :on_red], # missing
31
+ '?' => [:yellow], # not tracked
32
+ 'I' => [:dim, :white], # ignored
33
+ }
34
+
35
+ # File indentation
36
+ FILE_INDENT = " • "
37
+
38
+
39
+ ### Define version-control tasks
40
+ def define_tasks
41
+ super if defined?( super )
42
+
43
+ return unless self.is_git_working_copy?
44
+
45
+ # :TODO: Should be refactored up with the same code in the hg lib.
46
+ file COMMIT_MSG_FILE.to_s do |task|
47
+ commit_log = Pathname( task.name )
48
+
49
+ edit_git_commit_log( commit_log )
50
+ unless commit_log.size?
51
+ self.prompt.error "Empty commit message; aborting."
52
+ commit_log.unlink if commit_log.exist?
53
+ abort
54
+ end
55
+ end
56
+
57
+ CLEAN.include( COMMIT_MSG_FILE.to_s )
58
+
59
+ namespace :git do
60
+
61
+ desc "Prepare for a new release"
62
+ task( :prerelease, &method(:do_git_prerelease) )
63
+
64
+ desc "Check for new files and offer to add/ignore/delete them."
65
+ task( :newfiles, &method(:do_git_newfiles) )
66
+ task :add => :newfiles
67
+
68
+ desc "Pull and update from the default repo"
69
+ task( :pull, &method(:do_git_pull) )
70
+
71
+ desc "Pull and update without confirmation"
72
+ task( :pull_without_confirmation, &method(:do_git_pull_without_confirmation) )
73
+
74
+ desc "Update to tip"
75
+ task( :update, &method(:do_git_update) )
76
+
77
+ desc "Clobber all changes (git up -C)"
78
+ task( :update_and_clobber, &method(:do_git_update_and_clobber) )
79
+
80
+ desc "Git-specific pre-checkin hook"
81
+ task :precheckin => [ :pull, :newfiles, :check_for_changes ]
82
+
83
+ desc "Check the current code in if tests pass"
84
+ task( :checkin => COMMIT_MSG_FILE.to_s, &method(:do_git_checkin) )
85
+
86
+ desc "Git-specific pre-release hook"
87
+ task :prerelease => 'git:check_history'
88
+
89
+ desc "Git-specific post-release hook"
90
+ task( :postrelease, &method(:do_git_postrelease) )
91
+
92
+ desc "Push to the default origin repo (if there is one)"
93
+ task( :push, &method(:do_git_push) )
94
+
95
+ desc "Push to the default repo without confirmation"
96
+ task :push_without_confirmation do |task, args|
97
+ self.git.push
98
+ end
99
+
100
+ desc "Check the history file to ensure it contains an entry for each release tag"
101
+ task( :check_history, &method(:do_git_check_history) )
102
+
103
+ desc "Generate and edit a new version entry in the history file"
104
+ task( :update_history, &method(:do_git_update_history) )
105
+
106
+ task( :check_for_changes, &method(:do_git_check_for_changes) )
107
+ task( :debug, &method(:do_git_debug) )
108
+ end
109
+
110
+
111
+ # Hook some generic tasks to the mercurial-specific ones
112
+ task :checkin => 'git:checkin'
113
+ task :precheckin => 'git:precheckin'
114
+
115
+ task :prerelease => 'git:prerelease'
116
+ task :postrelease => 'git:postrelease'
117
+
118
+ desc "Update the history file with the changes since the last version tag."
119
+ task :update_history => 'git:update_history'
120
+
121
+ task :debug => 'git:debug'
122
+ rescue ::Exception => err
123
+ $stderr.puts "%s while defining Git tasks: %s" % [ err.class.name, err.message ]
124
+ raise
125
+ end
126
+
127
+
128
+ ### Returns +true+ if the current directory looks like a Git working copy.
129
+ def is_git_working_copy?
130
+ return File.directory?( '.git' )
131
+ end
132
+
133
+
134
+ ### The body of the git:prerelease task.
135
+ def do_git_prerelease( task, args )
136
+ uncommitted_files = self.git.status( n: true )
137
+ unless uncommitted_files.empty?
138
+ self.show_git_file_statuses( uncommitted_files )
139
+
140
+ fail unless self.prompt.yes?( "Release anyway?" ) do |q|
141
+ q.default( false )
142
+ end
143
+
144
+ self.prompt.warn "Okay, releasing with uncommitted versions."
145
+ end
146
+
147
+ pkg_version_tag = self.current_git_version_tag
148
+ rev = self.git.identity.id
149
+
150
+ # Look for a tag for the current release version, and if it exists abort
151
+ if self.git.tags.find {|tag| tag.name == pkg_version_tag }
152
+ self.prompt.error "Version #{self.version} already has a tag."
153
+ fail
154
+ end
155
+
156
+ # Tag the current rev
157
+ self.prompt.ok "Tagging rev %s as %s" % [ rev, pkg_version_tag ]
158
+ self.git.tag( pkg_version_tag, rev: rev )
159
+
160
+ # Sign the tag
161
+ if self.git.extension_enabled?( :gpg )
162
+ if self.prompt.yes?( "Sign %s?" % [pkg_version_tag] )
163
+ self.git.sign( pkg_version_tag, message: "Signing %s" % [pkg_version_tag] )
164
+ end
165
+ end
166
+ end
167
+
168
+
169
+ ### The body of the git:postrelease task.
170
+ def do_git_postrelease( task, args )
171
+ if self.git.status( 'checksum', unknown: true ).any?
172
+ self.prompt.say "Adding release artifacts..."
173
+ self.git.add( 'checksum' )
174
+ self.git.commit( 'checksum', message: "Adding release checksum." )
175
+ end
176
+
177
+ if self.prompt.yes?( "Move released changesets to public phase?" )
178
+ self.prompt.say "Publicising changesets..."
179
+ self.git.phase( public: true )
180
+ end
181
+
182
+ if self.git.extension_enabled?( :topic )
183
+ current_topic = self.git.topic
184
+ if current_topic && self.prompt.yes?( "Clear the current topic (%s)?" %[current_topic] )
185
+ self.git.topic( clear: true )
186
+ end
187
+ end
188
+
189
+ Rake::Task['git:push'].invoke
190
+ end
191
+
192
+
193
+ ### The body of the git:newfiles task.
194
+ def do_git_newfiles( task, args )
195
+ self.prompt.say "Checking for new files..."
196
+ status = self.git.status
197
+
198
+ files_to_add = status.changed.keys
199
+ files_to_ignore = []
200
+ files_to_delete = []
201
+
202
+ status.untracked.each do |path, status_file|
203
+ description = " %s: untracked" % [ path ]
204
+ action = self.prompt.select( description ) do |menu|
205
+ menu.choice "add", :a
206
+ menu.choice "ignore", :i
207
+ menu.choice "skip", :s
208
+ menu.choice "delete", :d
209
+ end
210
+
211
+ case action
212
+ when :a
213
+ files_to_add << path
214
+ when :i
215
+ files_to_ignore << path
216
+ when :d
217
+ files_to_delete << path
218
+ end
219
+ end
220
+
221
+ unless files_to_add.empty?
222
+ $stderr.puts "Adding: %p" % [ files_to_add ]
223
+ self.git.add( files_to_add )
224
+ end
225
+
226
+ unless files_to_ignore.empty?
227
+ git_ignore_files( *files_to_ignore )
228
+ end
229
+
230
+ unless files_to_delete.empty?
231
+ delete_extra_files( *files_to_delete )
232
+ end
233
+ end
234
+
235
+
236
+ ### The body of the git:pull task.
237
+ def do_git_pull( task, args )
238
+ origin = self.git.remote
239
+
240
+ if ( origin_url = origin.url )
241
+ if self.prompt.yes?( "Pull and update from '#{origin_url}'?" )
242
+ self.prompt.say "Fetching..."
243
+ self.git.fetch( 'origin', prune: true )
244
+ self.prompt.say "Pulling..."
245
+ self.git.pull( 'origin', self.git.current_branch )
246
+ end
247
+ else
248
+ trace "Skipping pull: No 'origin' remote."
249
+ end
250
+ end
251
+
252
+
253
+ ### The body of the git:pull_without_confirmation task.
254
+ def do_git_pull_without_confirmation( task, args )
255
+ self.git.pull
256
+ end
257
+
258
+
259
+ ### The body of the git:update task.
260
+ def do_git_update( task, args )
261
+ self.git.pull_update
262
+ end
263
+
264
+
265
+ ### The body of the git:update_and_clobber task.
266
+ def do_git_update_and_clobber( task, args )
267
+ self.git.update( clean: true )
268
+ end
269
+
270
+
271
+ ### The body of the checkin task.
272
+ def do_git_checkin( task, args )
273
+ commit_msg = COMMIT_MSG_FILE.read.strip
274
+
275
+ self.prompt.say( "---", color: :cyan )
276
+ self.prompt.say( commit_msg )
277
+ self.prompt.say( "---", color: :cyan )
278
+
279
+ if self.prompt.yes?( "Continue with checkin?" )
280
+ self.git.commit( COMMIT_MSG_FILE.read )
281
+ rm_f COMMIT_MSG_FILE
282
+ else
283
+ abort
284
+ end
285
+ Rake::Task[ 'git:push' ].invoke
286
+ end
287
+
288
+
289
+ ### The body of the push task.
290
+ def do_git_push( task, args )
291
+ git = self.git
292
+ origin = git.remote
293
+
294
+ if (origin_url = origin.url)
295
+ if self.prompt.yes?( "Push to '#{origin_url}'?" ) {|q| q.default(false) }
296
+ unless git.is_remote_branch?( git.current_branch )
297
+ if self.prompt.yes?( "Create tracking branch?" ) {|q| q.default(true) }
298
+ tracking_branch = "origin/%s" % [ git.current_branch ]
299
+ git.cmd( 'branch', ['-u', tracking_branch] )
300
+ end
301
+ end
302
+
303
+ git.push( 'origin', git.current_branch )
304
+ self.prompt.ok "Done."
305
+ else
306
+ abort
307
+ end
308
+ else
309
+ trace "Skipping push: No 'default' path."
310
+ end
311
+ end
312
+
313
+
314
+ ### Check the history file against the list of release tags in the working copy
315
+ ### and ensure there's an entry for each tag.
316
+ def do_git_check_history( task, args )
317
+ unless self.history_file.readable?
318
+ self.prompt.error "History file is missing or unreadable."
319
+ abort
320
+ end
321
+
322
+ self.prompt.say "Checking history..."
323
+ missing_tags = self.get_git_unhistoried_version_tags
324
+
325
+ unless missing_tags.empty?
326
+ self.prompt.error "%s needs updating; missing entries for tags: %s" %
327
+ [ self.history_file, missing_tags.join(', ') ]
328
+ abort
329
+ end
330
+ end
331
+
332
+
333
+ ### Check the status of the repo and ensure there are outstanding changes. If there
334
+ ### are no changes, abort.
335
+ def do_git_check_for_changes( task, args )
336
+ # :FIXME: Figure out a better way to do this.
337
+ unless self.git.status.any?( &:type )
338
+ self.prompt.error "Working copy is clean."
339
+ abort
340
+ end
341
+ end
342
+
343
+
344
+ ### Generate a new history file entry for the current version.
345
+ def do_git_update_history( task, args ) # Needs refactoring
346
+ unless self.history_file.readable?
347
+ self.prompt.error "History file is missing or unreadable."
348
+ abort
349
+ end
350
+
351
+ version_tag = self.current_git_version_tag
352
+ previous_tag = self.previous_git_version_tag
353
+ self.prompt.say "Updating history for %s..." % [ version_tag ]
354
+
355
+ if self.get_history_file_versions.include?( version_tag )
356
+ self.trace "History file already includes a section for %s" % [ version_tag ]
357
+ abort
358
+ end
359
+
360
+ header, rest = self.history_file.read( encoding: 'utf-8' ).
361
+ split( /(?<=^---)/m, 2 )
362
+
363
+ self.trace "Rest is: %p" % [ rest ]
364
+ if !rest || rest.empty?
365
+ self.prompt.warn "History file needs a header with a `---` marker to support updating."
366
+ self.prompt.say "Adding an auto-generated one."
367
+ rest = header
368
+ header = self.load_and_render_template( 'History.erb', self.history_file )
369
+ end
370
+
371
+ header_char = self.header_char_for( self.history_file )
372
+ ext = self.history_file.extname
373
+ log_entries = if previous_tag
374
+ self.git.log( rev: "#{previous_tag}~-2::" )
375
+ else
376
+ self.git.log
377
+ end
378
+
379
+ Tempfile.create( ['History', ext], encoding: 'utf-8' ) do |tmp_copy|
380
+ tmp_copy.print( header )
381
+ tmp_copy.puts
382
+
383
+ tmp_copy.puts "%s %s [%s] %s" % [
384
+ header_char * 2,
385
+ version_tag,
386
+ Date.today.strftime( '%Y-%m-%d' ),
387
+ self.authors.first,
388
+ ]
389
+
390
+ tmp_copy.puts
391
+ log_entries.each do |entry|
392
+ tmp_copy.puts "- %s" % [ entry.message ]
393
+ end
394
+ tmp_copy.puts
395
+ tmp_copy.puts
396
+
397
+ tmp_copy.print( rest )
398
+ tmp_copy.close
399
+
400
+ TTY::Editor.open( tmp_copy.path )
401
+
402
+ if File.size?( tmp_copy.path )
403
+ cp( tmp_copy.path, self.history_file )
404
+ else
405
+ self.prompt.error "Empty file: aborting."
406
+ end
407
+ end
408
+
409
+ end
410
+
411
+
412
+ ### Show debugging information.
413
+ def do_git_debug( task, args )
414
+ self.prompt.say( "Git Info", color: :bright_green )
415
+
416
+ if self.is_git_working_copy?
417
+ self.prompt.say( "Release tag prefix: " )
418
+ self.prompt.say( self.release_tag_prefix, color: :bold )
419
+
420
+ self.prompt.say( "Version tags:" )
421
+ self.get_git_version_tag_names.each do |tag|
422
+ self.prompt.say( '- ' )
423
+ self.prompt.say( tag, color: :bold )
424
+ end
425
+
426
+ self.prompt.say( "History file versions:" )
427
+ self.get_history_file_versions.each do |tag|
428
+ self.prompt.say( '- ' )
429
+ self.prompt.say( tag, color: :bold )
430
+ end
431
+
432
+ self.prompt.say( "Unhistoried version tags:" )
433
+ self.get_git_unhistoried_version_tags.each do |tag|
434
+ self.prompt.say( '- ' )
435
+ self.prompt.say( tag, color: :bold )
436
+ end
437
+ else
438
+ self.prompt.say( "Doesn't appear to be a Git repository." )
439
+ end
440
+
441
+ self.prompt.say( "\n" )
442
+ end
443
+
444
+ #
445
+ # utility methods
446
+ #
447
+
448
+ ### Return a Git::Repo for the directory rake was invoked in, creating it if
449
+ ### necessary.
450
+ def git
451
+ @git ||= Git.open( Rake::DevEiate::PROJECT_DIR )
452
+ end
453
+
454
+
455
+ ### Given a +status_hash+ like that returned by Git::Repo.status, return a
456
+ ### string description of the files and their status.
457
+ def show_git_file_statuses( statuses )
458
+ lines = statuses.map do |entry|
459
+ status_color = STATUS_COLORS[ entry.status ]
460
+ " %s: %s" % [
461
+ self.pastel.white( entry.path.to_s ),
462
+ self.pastel.decorate( entry.status_description, *status_color ),
463
+ ]
464
+ end
465
+
466
+ self.prompt.say( self.pastel.headline "Uncommitted files:" )
467
+ self.prompt.say( lines.join("\n") )
468
+ end
469
+
470
+
471
+ ### Fetch the name of the current version's tag.
472
+ def current_git_version_tag
473
+ return [ self.release_tag_prefix, self.version ].join
474
+ end
475
+
476
+
477
+ ### Fetch the name of the tag for the previous version.
478
+ def previous_git_version_tag
479
+ return self.get_git_version_tag_names.first
480
+ end
481
+
482
+
483
+ ### Fetch the list of names of tags that match the versioning scheme of this
484
+ ### project.
485
+ def get_git_version_tag_names
486
+ tag_pattern = self.release_tag_pattern
487
+ return self.git.tags.map( &:name ).grep( tag_pattern )
488
+ end
489
+
490
+
491
+ ### Read the list of tags and return any that don't have a corresponding section
492
+ ### in the history file.
493
+ def get_git_unhistoried_version_tags( include_current_version: true )
494
+ release_tags = self.get_git_version_tag_names
495
+ release_tags.unshift( self.current_git_version_tag ) if include_current_version
496
+
497
+ self.get_history_file_versions.each do |tag|
498
+ release_tags.delete( tag )
499
+ end
500
+
501
+ return release_tags
502
+ end
503
+
504
+
505
+ ### Generate a commit log and invoke the user's editor on it.
506
+ def edit_git_commit_log( logfile )
507
+ diff = self.git.diff
508
+
509
+ TTY::Editor.open( logfile, text: diff )
510
+ end
511
+
512
+
513
+ ### Add the list of +pathnames+ to the .gitignore list.
514
+ def git_ignore_files( *pathnames )
515
+ self.trace "Ignoring %d files." % [ pathnames.length ]
516
+
517
+ IGNORE_FILE.open( File::CREAT|File::WRONLY|File::APPEND, 0644 ) do |fh|
518
+ fh.puts( pathnames )
519
+ end
520
+ end
521
+
522
+ end # module Rake::DevEiate::Git
523
+
524
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rake-deveiate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 0.16.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Granger
@@ -219,6 +219,7 @@ files:
219
219
  - lib/rake/deveiate/gem_dep_finder.rb
220
220
  - lib/rake/deveiate/gemspec.rb
221
221
  - lib/rake/deveiate/generate.rb
222
+ - lib/rake/deveiate/git.rb
222
223
  - lib/rake/deveiate/hg.rb
223
224
  - lib/rake/deveiate/packaging.rb
224
225
  - lib/rake/deveiate/releases.rb
metadata.gz.sig CHANGED
Binary file