reissue 0.5.0 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -4
- data/README.md +4 -0
- data/lib/reissue/gem.rb +24 -0
- data/lib/reissue/rake.rb +102 -11
- data/lib/reissue/runbook.rb +27 -7
- data/lib/reissue/version.rb +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6c5e8636f0e88ac91af9c6dec7033c12a62e55d6c47a0eb2212dc03d51877058
|
|
4
|
+
data.tar.gz: a83714b57eb820b00a4b3cbfdab77640d5062f7d3e00aba0b52aca83d89cbad0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 63a416ec48050137cd7ee8ecd1d295395aa2e32b243fbb0835aecd185a48ddf36abebfe1c979450ef6be9cec46a67c2163896fba5b265874dc7b13eb1bc76ad5
|
|
7
|
+
data.tar.gz: d6b7f8c2e94997cd952c8e84aa74f29659d06cae56d338e20c10d37405ee1eb8dbf5615688268d366855942d1745bd36d7cd2f8881a1c29104c6045ad29a1947
|
data/CHANGELOG.md
CHANGED
|
@@ -5,14 +5,23 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
7
7
|
|
|
8
|
-
## [0.
|
|
8
|
+
## [0.5.1] - 2026-07-24
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
-
-
|
|
12
|
+
- reissue:next_version task reports the version the next release would use without changing anything (9feb212)
|
|
13
|
+
- reissue:initialize[runbook] scaffolds a starter runbook file (dfa521a)
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
- Runbook finalize and new-version reset preserve a custom title and preamble (a0d874c)
|
|
15
18
|
|
|
16
19
|
### Fixed
|
|
17
20
|
|
|
18
|
-
- changelog
|
|
21
|
+
- Releases no longer publish a version whose changelog entry and git tag name the previous version (acf4c1a)
|
|
22
|
+
|
|
23
|
+
## [0.4.23] - 2026-07-21
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Optional runbook_file setting maintains a post-release runbook populated from Runbook: git trailers or direct edits (b41ba15)
|
data/README.md
CHANGED
|
@@ -357,6 +357,8 @@ Reissue::Task.create :reissue do |task|
|
|
|
357
357
|
end
|
|
358
358
|
```
|
|
359
359
|
|
|
360
|
+
You don't have to create the file yourself — the first `rake reissue` or `rake reissue:finalize` writes it when `runbook_file` is set. To start from a template you can customize, run `rake reissue:initialize[runbook]`, which creates `RUNBOOK.md` (pass a different name, e.g. `rake reissue:initialize[OPERATIONS.md]`, to use another path).
|
|
361
|
+
|
|
360
362
|
The runbook holds only the latest release. Items are checkboxes so operators can tick them off:
|
|
361
363
|
|
|
362
364
|
```markdown
|
|
@@ -370,6 +372,8 @@ Steps to perform after releasing the version below.
|
|
|
370
372
|
- [ ] Re-index search documents (def5678)
|
|
371
373
|
```
|
|
372
374
|
|
|
375
|
+
The title and the preamble beneath it are yours to edit. Give the file a custom heading and intro and reissue preserves them across finalize and new-version resets — only the version heading and the checklist are rewritten, the same way a custom preamble survives in `CHANGELOG.md`.
|
|
376
|
+
|
|
373
377
|
### Adding Runbook Items
|
|
374
378
|
|
|
375
379
|
Add `Runbook:` trailers to commit messages (case-insensitive, independent of the `fragment` setting):
|
data/lib/reissue/gem.rb
CHANGED
|
@@ -7,6 +7,30 @@ module Reissue
|
|
|
7
7
|
super
|
|
8
8
|
@updated_paths << "checksums"
|
|
9
9
|
end
|
|
10
|
+
|
|
11
|
+
# Keep bundler's gemspec in step with the bump.
|
|
12
|
+
#
|
|
13
|
+
# Bundler loads the gemspec when bundler/gem_tasks is required, well before
|
|
14
|
+
# reissue:bump rewrites the version file. `gem build` shells out and re-reads
|
|
15
|
+
# the file, so the gem itself carries the bumped version, but the release tag
|
|
16
|
+
# and bundler's confirmation messages read the copy held in memory. Without
|
|
17
|
+
# this they name the version from before the bump, and the release publishes
|
|
18
|
+
# one version under another version's tag.
|
|
19
|
+
def apply_version_bump(updater, bump)
|
|
20
|
+
new_version = super
|
|
21
|
+
sync_bundler_gemspec_version(new_version)
|
|
22
|
+
new_version
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @param new_version [String] the version the bump landed on
|
|
26
|
+
def sync_bundler_gemspec_version(new_version)
|
|
27
|
+
return unless defined?(Bundler::GemHelper)
|
|
28
|
+
|
|
29
|
+
helper = Bundler::GemHelper.instance
|
|
30
|
+
return unless helper&.gemspec
|
|
31
|
+
|
|
32
|
+
helper.gemspec.version = ::Gem::Version.new(new_version)
|
|
33
|
+
end
|
|
10
34
|
end
|
|
11
35
|
end
|
|
12
36
|
Reissue::Task.prepend Reissue::Gem
|
data/lib/reissue/rake.rb
CHANGED
|
@@ -188,6 +188,78 @@ module Reissue
|
|
|
188
188
|
end
|
|
189
189
|
end
|
|
190
190
|
|
|
191
|
+
# The version the next release would use, derived exactly the way reissue:bump
|
|
192
|
+
# derives it, without writing anything.
|
|
193
|
+
#
|
|
194
|
+
# @return [Gem::Version] the version a release would publish
|
|
195
|
+
def next_release_version
|
|
196
|
+
require_relative "fragment_handler"
|
|
197
|
+
require_relative "version_updater"
|
|
198
|
+
|
|
199
|
+
# reissue:bump only consults git trailers for :git fragments.
|
|
200
|
+
handler = (Reissue::FragmentHandler.for(:git, tag_pattern: tag_pattern) if fragment == :git)
|
|
201
|
+
tag_version = handler&.last_tag_version
|
|
202
|
+
bump = handler&.read_version_bump
|
|
203
|
+
updater = Reissue::VersionUpdater.new(version_file, version_redo_proc: version_redo_proc)
|
|
204
|
+
|
|
205
|
+
if deferred_versioning
|
|
206
|
+
# The version file holds "Unreleased"; finalize resolves the real version
|
|
207
|
+
# from the last tag and the trailer.
|
|
208
|
+
unless tag_version && bump
|
|
209
|
+
raise "Cannot determine the next version. Deferred versioning resolves it from " \
|
|
210
|
+
"the last version tag and a Version: trailer, and one of those is missing."
|
|
211
|
+
end
|
|
212
|
+
return updater.redo(tag_version, bump)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
current_version = ::Gem::Version.new(File.read(version_file).match(Reissue::VersionUpdater::VERSION_MATCH)[0])
|
|
216
|
+
|
|
217
|
+
if bump.nil?
|
|
218
|
+
current_version
|
|
219
|
+
elsif tag_version && tag_version != current_version
|
|
220
|
+
# A post-release bump already moved the file past the tag, so the trailer
|
|
221
|
+
# only counts if it would land beyond where the file already is.
|
|
222
|
+
[updater.redo(tag_version, bump), current_version].max
|
|
223
|
+
else
|
|
224
|
+
updater.redo(current_version, bump)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Apply a version bump and leave the changelog agreeing with it.
|
|
229
|
+
#
|
|
230
|
+
# @param updater [Reissue::VersionUpdater] the updater to bump with
|
|
231
|
+
# @param bump [Symbol] the segment to bump
|
|
232
|
+
# @return [String] the version the bump landed on
|
|
233
|
+
def apply_version_bump(updater, bump)
|
|
234
|
+
new_version = updater.call(bump)
|
|
235
|
+
realign_unreleased_changelog(new_version)
|
|
236
|
+
bundle
|
|
237
|
+
puts "Version bumped (#{bump}) to #{new_version}"
|
|
238
|
+
new_version
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Point the changelog's unreleased entry at the version just bumped to.
|
|
242
|
+
#
|
|
243
|
+
# The bump moves the version file forward, but the unreleased entry still
|
|
244
|
+
# carries the number from the previous cycle. reissue:finalize dates whatever
|
|
245
|
+
# that entry says, so leaving it alone publishes a gem built at the new
|
|
246
|
+
# version while the only dated changelog entry names the old one.
|
|
247
|
+
#
|
|
248
|
+
# @param new_version [String] the version the bump landed on
|
|
249
|
+
def realign_unreleased_changelog(new_version)
|
|
250
|
+
return unless changelog_file && File.exist?(changelog_file)
|
|
251
|
+
|
|
252
|
+
changelog = Reissue::Parser.parse(File.read(changelog_file))
|
|
253
|
+
unreleased = changelog["versions"].find { |version| version["date"] == "Unreleased" }
|
|
254
|
+
return if unreleased.nil? || unreleased["version"] == new_version
|
|
255
|
+
|
|
256
|
+
unreleased["version"] = new_version
|
|
257
|
+
|
|
258
|
+
changelog_updater = Reissue::ChangelogUpdater.new(changelog_file)
|
|
259
|
+
changelog_updater.instance_variable_set(:@changelog, changelog)
|
|
260
|
+
changelog_updater.write(changelog_file)
|
|
261
|
+
end
|
|
262
|
+
|
|
191
263
|
# Check if there are staged changes ready to commit.
|
|
192
264
|
def changes_to_commit?
|
|
193
265
|
_, _, status = Open3.capture3("git diff --cached --quiet")
|
|
@@ -359,6 +431,15 @@ module Reissue
|
|
|
359
431
|
end
|
|
360
432
|
end
|
|
361
433
|
|
|
434
|
+
# Preview reports; it does not fail. next_release_version raises when
|
|
435
|
+
# deferred versioning has no tag or trailer to resolve from.
|
|
436
|
+
previewed_version = begin
|
|
437
|
+
next_release_version
|
|
438
|
+
rescue RuntimeError
|
|
439
|
+
nil
|
|
440
|
+
end
|
|
441
|
+
puts "Version to be released: #{previewed_version || "cannot be determined"}\n\n"
|
|
442
|
+
|
|
362
443
|
entries = handler.read
|
|
363
444
|
|
|
364
445
|
if entries.empty?
|
|
@@ -418,6 +499,11 @@ module Reissue
|
|
|
418
499
|
end
|
|
419
500
|
end
|
|
420
501
|
|
|
502
|
+
desc "Print the version the next release would use, without changing anything"
|
|
503
|
+
task "#{name}:next_version" do
|
|
504
|
+
puts next_release_version
|
|
505
|
+
end
|
|
506
|
+
|
|
421
507
|
desc "Bump version based on git trailers"
|
|
422
508
|
task "#{name}:bump" do
|
|
423
509
|
next if deferred_versioning
|
|
@@ -441,9 +527,7 @@ module Reissue
|
|
|
441
527
|
if tag_version && current_version == tag_version
|
|
442
528
|
if bump
|
|
443
529
|
updater = Reissue::VersionUpdater.new(version_file, version_redo_proc: version_redo_proc)
|
|
444
|
-
updater
|
|
445
|
-
bundle
|
|
446
|
-
puts "Version bumped (#{bump}) to #{updater.instance_variable_get(:@new_version)}"
|
|
530
|
+
apply_version_bump(updater, bump)
|
|
447
531
|
end
|
|
448
532
|
elsif tag_version && current_version != tag_version
|
|
449
533
|
if bump
|
|
@@ -451,9 +535,7 @@ module Reissue
|
|
|
451
535
|
desired_version = updater.redo(tag_version, bump)
|
|
452
536
|
|
|
453
537
|
if desired_version > current_version
|
|
454
|
-
updater
|
|
455
|
-
bundle
|
|
456
|
-
puts "Version bumped (#{bump}) to #{updater.instance_variable_get(:@new_version)}"
|
|
538
|
+
apply_version_bump(updater, bump)
|
|
457
539
|
else
|
|
458
540
|
puts "Version already bumped (#{tag_version} → #{current_version}), skipping"
|
|
459
541
|
end
|
|
@@ -462,9 +544,7 @@ module Reissue
|
|
|
462
544
|
end
|
|
463
545
|
elsif bump
|
|
464
546
|
updater = Reissue::VersionUpdater.new(version_file, version_redo_proc: version_redo_proc)
|
|
465
|
-
updater
|
|
466
|
-
bundle
|
|
467
|
-
puts "Version bumped (#{bump}) to #{updater.instance_variable_get(:@new_version)}"
|
|
547
|
+
apply_version_bump(updater, bump)
|
|
468
548
|
end
|
|
469
549
|
end
|
|
470
550
|
end
|
|
@@ -474,8 +554,8 @@ end
|
|
|
474
554
|
# Define the reissue:initialize task as a standalone task
|
|
475
555
|
# This works without any configuration, allowing users to bootstrap their project
|
|
476
556
|
namespace :reissue do
|
|
477
|
-
desc "Initialize reissue by creating a CHANGELOG.md and showing Rakefile configuration"
|
|
478
|
-
task :initialize do
|
|
557
|
+
desc "Initialize reissue by creating a CHANGELOG.md and showing Rakefile configuration. Pass [runbook] to also create a RUNBOOK.md."
|
|
558
|
+
task :initialize, [:runbook] do |task, args|
|
|
479
559
|
changelog_file = "CHANGELOG.md"
|
|
480
560
|
|
|
481
561
|
if File.exist?(changelog_file)
|
|
@@ -485,6 +565,16 @@ namespace :reissue do
|
|
|
485
565
|
puts "✓ Created #{changelog_file}"
|
|
486
566
|
end
|
|
487
567
|
|
|
568
|
+
if args[:runbook]
|
|
569
|
+
runbook_file = (args[:runbook] == "runbook") ? "RUNBOOK.md" : args[:runbook]
|
|
570
|
+
if File.exist?(runbook_file)
|
|
571
|
+
puts "✓ #{runbook_file} already exists"
|
|
572
|
+
else
|
|
573
|
+
Reissue::Runbook.new(runbook_file).generate
|
|
574
|
+
puts "✓ Created #{runbook_file}"
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
488
578
|
puts
|
|
489
579
|
puts "Add the following to your Rakefile to enable reissue tasks:"
|
|
490
580
|
puts
|
|
@@ -514,6 +604,7 @@ namespace :reissue do
|
|
|
514
604
|
|
|
515
605
|
# Post-release runbook (mainly for applications)
|
|
516
606
|
# task.runbook_file = "RUNBOOK.md" # Maintain a runbook from Runbook: git trailers
|
|
607
|
+
# # Seed a starter file with: rake reissue:initialize[runbook]
|
|
517
608
|
|
|
518
609
|
# Git workflow options
|
|
519
610
|
task.commit = true # Auto-commit version bumps
|
data/lib/reissue/runbook.rb
CHANGED
|
@@ -6,15 +6,20 @@ module Reissue
|
|
|
6
6
|
class Runbook
|
|
7
7
|
ITEM_PATTERN = /^- (?:\[(?<checked>[ xX])\] )?(?<text>.+)$/
|
|
8
8
|
|
|
9
|
+
DEFAULT_TITLE = "Runbook"
|
|
10
|
+
DEFAULT_PREAMBLE = "Steps to perform after releasing the version below."
|
|
11
|
+
|
|
9
12
|
def initialize(runbook_file)
|
|
10
13
|
@runbook_file = runbook_file
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
attr_reader :runbook_file
|
|
14
17
|
|
|
15
|
-
# Writes a header-only template for the given version
|
|
18
|
+
# Writes a header-only template for the given version, preserving any
|
|
19
|
+
# custom title and preamble already in the file.
|
|
16
20
|
def generate(version: "Unreleased")
|
|
17
|
-
|
|
21
|
+
title, preamble = parsed_header
|
|
22
|
+
File.write(runbook_file, template(heading(version), title:, preamble:))
|
|
18
23
|
end
|
|
19
24
|
|
|
20
25
|
alias_method :clear, :generate
|
|
@@ -25,18 +30,33 @@ module Reissue
|
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
# Stamps the header with the release version and date, merging directly
|
|
28
|
-
# edited items with trailer items (deduplicated by text).
|
|
33
|
+
# edited items with trailer items (deduplicated by text). Preserves any
|
|
34
|
+
# custom title and preamble already in the file.
|
|
29
35
|
def finalize(version:, date:, trailer_items: [])
|
|
36
|
+
title, preamble = parsed_header
|
|
30
37
|
merged = parsed_items
|
|
31
38
|
texts = merged.map { |item| item[:text] }
|
|
32
39
|
trailer_items.each do |text|
|
|
33
40
|
merged << {text: text, checked: false} unless texts.include?(text)
|
|
34
41
|
end
|
|
35
|
-
File.write(runbook_file, template(heading(version, date), merged))
|
|
42
|
+
File.write(runbook_file, template(heading(version, date), merged, title:, preamble:))
|
|
36
43
|
end
|
|
37
44
|
|
|
38
45
|
private
|
|
39
46
|
|
|
47
|
+
# Returns the [title, preamble] found before the first version heading,
|
|
48
|
+
# falling back to the defaults when the file is missing or header-only.
|
|
49
|
+
def parsed_header
|
|
50
|
+
return [DEFAULT_TITLE, DEFAULT_PREAMBLE] unless File.exist?(runbook_file)
|
|
51
|
+
|
|
52
|
+
header_chunk = File.read(runbook_file).split(/^## /, 2).first.to_s
|
|
53
|
+
title_match = header_chunk.match(/^#\s+(?<title>.+)$/)
|
|
54
|
+
title = title_match ? title_match[:title].strip : DEFAULT_TITLE
|
|
55
|
+
preamble = (title_match ? header_chunk.sub(title_match[0], "") : header_chunk).strip
|
|
56
|
+
preamble = DEFAULT_PREAMBLE if preamble.empty?
|
|
57
|
+
[title, preamble]
|
|
58
|
+
end
|
|
59
|
+
|
|
40
60
|
def parsed_items
|
|
41
61
|
return [] unless File.exist?(runbook_file)
|
|
42
62
|
|
|
@@ -51,11 +71,11 @@ module Reissue
|
|
|
51
71
|
date ? "## [#{version}] - #{date}" : "## [#{version}]"
|
|
52
72
|
end
|
|
53
73
|
|
|
54
|
-
def template(heading, items = [])
|
|
74
|
+
def template(heading, items = [], title: DEFAULT_TITLE, preamble: DEFAULT_PREAMBLE)
|
|
55
75
|
header = <<~MARKDOWN
|
|
56
|
-
#
|
|
76
|
+
# #{title}
|
|
57
77
|
|
|
58
|
-
|
|
78
|
+
#{preamble}
|
|
59
79
|
|
|
60
80
|
#{heading}
|
|
61
81
|
MARKDOWN
|
data/lib/reissue/version.rb
CHANGED