gem_kit-release 0.1.0 → 0.2.0

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.
@@ -23,7 +23,7 @@ module GemKit
23
23
  def deprecation_problems(version)
24
24
  project.load!
25
25
 
26
- Deprecate.pending(version).map do |entry|
26
+ GemKit::Deprecate.pending(version).map do |entry|
27
27
  "#{entry.removed_in.to_s.ljust(8)} #{entry}#{entry.declared_at ? "\n#{" " * 9}#{entry.declared_at}" : ""}"
28
28
  end
29
29
  end
@@ -32,7 +32,7 @@ module GemKit
32
32
  # way past, not worth blocking on.
33
33
  def upcoming_deprecations(version)
34
34
  project.load!
35
- Deprecate.upcoming(version)
35
+ GemKit::Deprecate.upcoming(version)
36
36
  end
37
37
 
38
38
  # Changelog format, plus "is there an entry for this version?" when one
@@ -96,12 +96,12 @@ describe "gem_kit/release/gate" do
96
96
 
97
97
  # Run a block with the deprecation registry isolated.
98
98
  isolated = lambda do |&block|
99
- saved = GemKit::Release::Deprecate.registry.dup
100
- GemKit::Release::Deprecate.registry.clear
99
+ saved = GemKit::Deprecate.registry.dup
100
+ GemKit::Deprecate.registry.clear
101
101
  begin
102
102
  block.call
103
103
  ensure
104
- GemKit::Release::Deprecate.registry.replace(saved)
104
+ GemKit::Deprecate.registry.replace(saved)
105
105
  end
106
106
  end
107
107
 
@@ -134,7 +134,7 @@ describe "gem_kit/release/gate" do
134
134
  File.write(path, clean_changelog)
135
135
 
136
136
  isolated.call do
137
- GemKit::Release::Deprecate.register(name: "Old", replacement: "New", removed_in: "1.0")
137
+ GemKit::Deprecate.register(name: "Old", replacement: "New", removed_in: "1.0")
138
138
  gate = GemKit::Release::Gate.new(stub_project.call(path))
139
139
 
140
140
  gate.release_problems("1.0.0").first.should.match(/deprecation due in 1\.0\.0: .*Old -> New/)
@@ -146,7 +146,7 @@ describe "gem_kit/release/gate" do
146
146
  it "does not block a bump on a deadline that has not arrived" do
147
147
  Dir.mktmpdir do |dir|
148
148
  isolated.call do
149
- GemKit::Release::Deprecate.register(name: "Old", replacement: "New", removed_in: "2.0")
149
+ GemKit::Deprecate.register(name: "Old", replacement: "New", removed_in: "2.0")
150
150
  gate = GemKit::Release::Gate.new(stub_project.call(File.join(dir, "CHANGELOG.md")))
151
151
 
152
152
  gate.bump_problems("1.1.0").should == []
@@ -0,0 +1,251 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "thor/group"
5
+
6
+ require_relative "../commands/command"
7
+
8
+ module GemKit
9
+ module Release
10
+ module Generators
11
+ # `gem kit setup` — a Thor generator, which is what Rails' own generators
12
+ # are. Using one rather than hand-rolled File.write buys the whole
13
+ # conflict protocol for free: `create`/`identical`/`conflict` status
14
+ # lines, a prompt offering diff/overwrite/skip when a file exists and
15
+ # differs, and --force / --skip / --pretend to answer that prompt ahead
16
+ # of time.
17
+ #
18
+ # It writes the two documents that describe this toolchain, rendered for
19
+ # the project's own name and versions. They are copies rather than links
20
+ # because a policy nobody can read in their own repo is a policy nobody
21
+ # follows, and because the examples are worth more with real numbers in
22
+ # them.
23
+ class Setup < Thor::Group
24
+ include Thor::Actions
25
+
26
+ # --force, --skip, --pretend and --quiet: Thor::Actions implements the
27
+ # behaviour but leaves declaring the switches to the generator.
28
+ add_runtime_options!
29
+
30
+ class_option :gem, type: :string,
31
+ desc: "Which gem, in a repository holding more than one gemspec"
32
+
33
+ def self.source_root
34
+ File.expand_path("../templates", __dir__)
35
+ end
36
+
37
+ # Written into the project, not the working directory: `gem kit setup`
38
+ # run from a subdirectory still belongs to the gem.
39
+ def self.banner = "gem kit setup"
40
+
41
+ # The policy is the repository's — one deprecation policy governs every
42
+ # gem in it — so DEPRECATIONS.md is written once, whichever gem you
43
+ # named.
44
+ def deprecations_document
45
+ template("DEPRECATIONS.md.erb", File.join(project.root, "DEPRECATIONS.md"))
46
+ end
47
+
48
+ # The process is the gem's: every command in it names a version and a
49
+ # changelog belonging to one gem. So a repository with two gems gets
50
+ # RELEASE-<gem>.md apiece, matching the changelogs.
51
+ def release_document
52
+ template("RELEASE.md.erb", File.join(project.root, release_document_name))
53
+ end
54
+
55
+ def where_to_link_them
56
+ say ""
57
+ say "Link them from your README and AGENTS.md so they are found."
58
+ end
59
+
60
+ # Everything below is machinery, not a step. Thor runs every public
61
+ # method of a Thor::Group in order, so helpers have to say so.
62
+ no_commands do
63
+ # Every command in a multi-gem repository needs to say which gem it
64
+ # means, so the examples show it.
65
+ # Pads a command so the trailing comments line up whatever the gem
66
+ # is called.
67
+ def step(command) = command.ljust(step_width)
68
+
69
+ def step_width
70
+ @step_width ||= [30, "gem kit changelog#{gem_flag} <VERSION>".length + 2].max
71
+ end
72
+
73
+ def multi_gem = project.multi_gem?
74
+ def tag_prefix = project.tag_prefix
75
+
76
+ # DEPRECATIONS.md is written once for the repository, so everything
77
+ # it says has to hold for every gem in it — no version numbers, and
78
+ # no link to one gem's changelog.
79
+ def deadline_rule
80
+ if multi_gem
81
+ "The usual deadline is the next major of the gem the name lives in."
82
+ else
83
+ "The current version is #{version}, so the usual deadline is #{next_major}."
84
+ end
85
+ end
86
+
87
+ def changelog_reference
88
+ multi_gem ? "that gem's changelog" : "`#{changelog}`"
89
+ end
90
+
91
+ def release_document_link
92
+ multi_gem ? "The RELEASE-<gem>.md beside it" : "[RELEASE.md](RELEASE.md)"
93
+ end
94
+
95
+ def gem_flag
96
+ project.multi_gem? ? " --gem #{project.name}" : ""
97
+ end
98
+
99
+ def release_document_name
100
+ project.multi_gem? ? "RELEASE-#{project.name}.md" : "RELEASE.md"
101
+ end
102
+
103
+ # The values the templates render against. Kept small and obvious — a
104
+ # template needing more than this is documenting the tool, not the
105
+ # project.
106
+ # DEPRECATIONS.md is the repository's, so what it is *about* must not
107
+ # depend on which gem you named — otherwise running setup for the
108
+ # second gem rewrites the first gem's document.
109
+ def subject
110
+ project.multi_gem? ? File.basename(project.root) : project.name
111
+ end
112
+
113
+ def name = project.name
114
+ def version = project.version.to_s
115
+ def next_major = project.next_major_version
116
+ def changelog = File.basename(project.changelog_path)
117
+ def test = project.test_command
118
+ def today = Time.now.strftime("%Y-%m-%d")
119
+
120
+ def version_file
121
+ project.version_file.sub("#{project.root}/", "")
122
+ end
123
+
124
+ def project
125
+ @project ||= Project.detect(Dir.pwd, name: options[:gem])
126
+ rescue Project::NotFound, Project::Ambiguous => error
127
+ raise Failure, error.message
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ __END__
136
+
137
+ describe "gem_kit/release/generators/setup" do
138
+ require_relative "../../../../spec/support/gem_kit_release_spec"
139
+ extend GemKitReleaseSpec
140
+
141
+ it "writes both documents into the project root" do
142
+ with_gem do |dir|
143
+ status, out, _err = invoke(["setup"], dir)
144
+
145
+ status.should == 0
146
+ out.should.match(/create.*DEPRECATIONS\.md/)
147
+ out.should.match(/create.*RELEASE\.md/)
148
+ File.exist?(File.join(dir, "DEPRECATIONS.md")).should.be.true
149
+ File.exist?(File.join(dir, "RELEASE.md")).should.be.true
150
+ end
151
+ end
152
+
153
+ it "renders them for this project's name and versions" do
154
+ with_gem(version: "4.1.0") do |dir|
155
+ invoke(["setup"], dir)
156
+
157
+ deprecations = File.read(File.join(dir, "DEPRECATIONS.md"))
158
+ deprecations.should.match(/A deprecation in demo is a \*\*dated promise\*\*/)
159
+ deprecations.should.match(/4\.1\.0, so the usual deadline is 5\.0/)
160
+ deprecations.should.not.match(/<%=/)
161
+
162
+ release = File.read(File.join(dir, "RELEASE.md"))
163
+ release.should.match(/Releasing demo/)
164
+ release.should.not.match(/<%=/)
165
+ end
166
+ end
167
+
168
+ # Thor::Actions reports an unchanged file as `identical` rather than
169
+ # rewriting it, which is the generator behaviour we switched to Thor for.
170
+ it "reports an unchanged document as identical rather than rewriting it" do
171
+ with_gem do |dir|
172
+ invoke(["setup"], dir)
173
+ before = File.mtime(File.join(dir, "RELEASE.md"))
174
+
175
+ _status, out, _err = invoke(["setup"], dir)
176
+
177
+ out.should.match(/identical.*RELEASE\.md/)
178
+ File.mtime(File.join(dir, "RELEASE.md")).should == before
179
+ end
180
+ end
181
+
182
+ it "--force overwrites a document that differs" do
183
+ with_gem do |dir|
184
+ File.write(File.join(dir, "RELEASE.md"), "mine\n")
185
+
186
+ status, out, _err = invoke(["setup", "--force"], dir)
187
+
188
+ status.should == 0
189
+ out.should.match(/force.*RELEASE\.md/)
190
+ File.read(File.join(dir, "RELEASE.md")).should.not == "mine\n"
191
+ end
192
+ end
193
+
194
+ it "--skip leaves a document that differs alone" do
195
+ with_gem do |dir|
196
+ File.write(File.join(dir, "RELEASE.md"), "mine\n")
197
+
198
+ invoke(["setup", "--skip"], dir).first.should == 0
199
+ File.read(File.join(dir, "RELEASE.md")).should == "mine\n"
200
+ end
201
+ end
202
+
203
+ it "--pretend writes nothing" do
204
+ with_gem do |dir|
205
+ invoke(["setup", "--pretend"], dir).first.should == 0
206
+ File.exist?(File.join(dir, "RELEASE.md")).should.be.false
207
+ end
208
+ end
209
+
210
+ it "is reachable by its alias" do
211
+ with_gem do |dir|
212
+ invoke(["init"], dir).first.should == 0
213
+ File.exist?(File.join(dir, "RELEASE.md")).should.be.true
214
+ end
215
+ end
216
+
217
+ # In a repository with several gems the process document is per-gem, but the
218
+ # policy is the repository's — so running setup for the second gem must not
219
+ # rewrite what the first one wrote.
220
+ it "writes a release document per gem and one policy for the repository" do
221
+ with_gem(second_gem: true) do |dir|
222
+ _status, first, _err = invoke(["setup", "--gem", "demo"], dir)
223
+ _status, second, _err = invoke(["setup", "--gem", "other"], dir)
224
+
225
+ first.should.match(/create.*DEPRECATIONS\.md/)
226
+ first.should.match(/create.*RELEASE-demo\.md/)
227
+
228
+ second.should.match(/identical.*DEPRECATIONS\.md/)
229
+ second.should.match(/create.*RELEASE-other\.md/)
230
+
231
+ File.exist?(File.join(dir, "RELEASE.md")).should.be.false
232
+ end
233
+ end
234
+
235
+ it "spells the commands with --gem when the repository holds several" do
236
+ with_gem(second_gem: true) do |dir|
237
+ invoke(["setup", "--gem", "demo"], dir)
238
+
239
+ release = File.read(File.join(dir, "RELEASE-demo.md"))
240
+ release.should.match(/gem kit bump --gem demo/)
241
+ release.should.match(/CHANGELOG-demo\.md/)
242
+ release.should.match(/demo-v1\.2\.3/)
243
+ end
244
+ end
245
+
246
+ it "reports a directory with no gemspec" do
247
+ Dir.mktmpdir do |dir|
248
+ invoke(["setup"], dir).first.should == 1
249
+ end
250
+ end
251
+ end
@@ -25,15 +25,37 @@ module GemKit
25
25
  class Project
26
26
  class NotFound < StandardError; end
27
27
 
28
+ # More than one gemspec and nothing to choose between them. A monorepo
29
+ # has to say which gem it means.
30
+ class Ambiguous < StandardError; end
31
+
28
32
  # Overridable settings. Each is nil until set, and nil means "infer".
29
33
  Config = Struct.new(:changelog, :version_file, :require_path, :test_command,
30
34
  :changelog_writer, keyword_init: true)
31
35
 
32
- def self.detect(dir = Dir.pwd, config: GemKit::Release.config)
33
- gemspec = Dir[File.join(dir, "*.gemspec")].sort.first
34
- raise NotFound, "no .gemspec in #{dir}" if gemspec.nil?
36
+ # `name` picks one gem out of a repository that holds several — matched
37
+ # against the gemspec's filename, so `--gem gem_kit` finds
38
+ # gem_kit.gemspec and not gem_kit-release.gemspec.
39
+ def self.detect(dir = Dir.pwd, name: nil, config: GemKit::Release.config)
40
+ gemspecs = Dir[File.join(dir, "*.gemspec")].sort
41
+ raise NotFound, "no .gemspec in #{dir}" if gemspecs.empty?
42
+
43
+ if name
44
+ match = gemspecs.find { |path| File.basename(path, ".gemspec") == name }
45
+ raise NotFound, "no #{name}.gemspec in #{dir}" if match.nil?
46
+
47
+ return new(match, config: config)
48
+ end
49
+
50
+ # Silently picking the first would mean bumping the wrong gem's version
51
+ # and only finding out on release.
52
+ if gemspecs.size > 1
53
+ names = gemspecs.map { |path| File.basename(path, ".gemspec") }
54
+ raise Ambiguous, "#{gemspecs.size} gemspecs in #{dir} (#{names.join(", ")}); " \
55
+ "name one with --gem"
56
+ end
35
57
 
36
- new(gemspec, config: config)
58
+ new(gemspecs.first, config: config)
37
59
  end
38
60
 
39
61
  attr_reader :root, :gemspec_path, :config
@@ -55,13 +77,38 @@ module GemKit
55
77
 
56
78
  def version = Gem::Version.new(spec.version.to_s)
57
79
 
58
- # The next major version — the default deadline for a deprecation.
80
+ # The next major version — the usual deadline for a deprecation. Written
81
+ # "5.0" rather than "5": it reads as a version in prose and in a
82
+ # declaration, and Gem::Version treats the two as equal anyway.
59
83
  def next_major_version
60
- Gem::Version.new(version.segments.first.to_s).bump.to_s
84
+ "#{version.segments.first.to_i + 1}.0"
61
85
  end
62
86
 
87
+ # Every gemspec beside this one, this one included. A repository with
88
+ # more than one releases each gem on its own schedule, and several things
89
+ # below have to know that.
90
+ def siblings
91
+ @siblings ||= Dir[File.join(root, "*.gemspec")].size
92
+ end
93
+
94
+ def multi_gem? = siblings > 1
95
+
96
+ # One changelog per gem. A shared file cannot work: the release gate asks
97
+ # whether the version being cut is the *topmost* released section, and
98
+ # two gems interleaved in one file means the older one never is — so the
99
+ # second gem could never be released.
63
100
  def changelog_path
64
- File.expand_path(config.changelog || "CHANGELOG.md", root)
101
+ File.expand_path(config.changelog || default_changelog, root)
102
+ end
103
+
104
+ def default_changelog
105
+ multi_gem? ? "CHANGELOG-#{name}.md" : "CHANGELOG.md"
106
+ end
107
+
108
+ # `v1.2.3` says which version but not which gem, which is fine until a
109
+ # repository holds two of them at the same version.
110
+ def tag_prefix
111
+ multi_gem? ? "#{name}-v" : "v"
65
112
  end
66
113
 
67
114
  # lib/gem_kit/release/version.rb for "gem_kit-release", lib/brute/version.rb
@@ -158,13 +205,50 @@ describe "gem_kit/release/project" do
158
205
 
159
206
  it "defaults the changelog to CHANGELOG.md in the project root" do
160
207
  with_project.call do |dir|
161
- GemKit::Release::Project.detect(dir).changelog_path.should == File.join(dir, "CHANGELOG.md")
208
+ project = GemKit::Release::Project.detect(dir)
209
+ project.multi_gem?.should.be.false
210
+ project.changelog_path.should == File.join(dir, "CHANGELOG.md")
211
+ project.tag_prefix.should == "v"
212
+ end
213
+ end
214
+
215
+ # One changelog and one tag namespace per gem, because a repository with two
216
+ # gems releases them separately.
217
+ it "gives each gem its own changelog and tag prefix when there are several" do
218
+ with_project.call(name: "demo") do |dir|
219
+ File.write(File.join(dir, "other.gemspec"), <<~RUBY)
220
+ Gem::Specification.new do |spec|
221
+ spec.name = "other"
222
+ spec.version = "9.9.9"
223
+ spec.authors = ["x"]
224
+ spec.summary = "x"
225
+ spec.files = []
226
+ end
227
+ RUBY
228
+
229
+ project = GemKit::Release::Project.detect(dir, name: "demo")
230
+ project.multi_gem?.should.be.true
231
+ project.changelog_path.should == File.join(dir, "CHANGELOG-demo.md")
232
+ project.tag_prefix.should == "demo-v"
233
+
234
+ GemKit::Release::Project.detect(dir, name: "other").changelog_path
235
+ .should == File.join(dir, "CHANGELOG-other.md")
236
+ end
237
+ end
238
+
239
+ it "an explicit changelog still overrides, in either kind of repository" do
240
+ with_project.call(name: "demo") do |dir|
241
+ File.write(File.join(dir, "other.gemspec"), "Gem::Specification.new { |s| s.name = \"other\" }")
242
+
243
+ config = GemKit::Release::Project::Config.new(changelog: "HISTORY.md")
244
+ GemKit::Release::Project.detect(dir, name: "demo", config: config).changelog_path
245
+ .should == File.join(dir, "HISTORY.md")
162
246
  end
163
247
  end
164
248
 
165
249
  it "bumps to the next major for the default deprecation deadline" do
166
250
  with_project.call(version: "4.1.0") do |dir|
167
- GemKit::Release::Project.detect(dir).next_major_version.should == "5"
251
+ GemKit::Release::Project.detect(dir).next_major_version.should == "5.0"
168
252
  end
169
253
  end
170
254
 
@@ -177,6 +261,39 @@ describe "gem_kit/release/project" do
177
261
  end
178
262
  end
179
263
 
264
+ it "picks one gem out of a repository holding several" do
265
+ with_project.call(name: "demo") do |dir|
266
+ File.write(File.join(dir, "other.gemspec"), <<~RUBY)
267
+ Gem::Specification.new do |spec|
268
+ spec.name = "other"
269
+ spec.version = "9.9.9"
270
+ spec.authors = ["x"]
271
+ spec.summary = "x"
272
+ spec.files = []
273
+ end
274
+ RUBY
275
+
276
+ GemKit::Release::Project.detect(dir, name: "demo").name.should == "demo"
277
+ GemKit::Release::Project.detect(dir, name: "other").version.should == Gem::Version.new("9.9.9")
278
+ end
279
+ end
280
+
281
+ it "refuses to guess between several gemspecs" do
282
+ with_project.call(name: "demo") do |dir|
283
+ File.write(File.join(dir, "other.gemspec"), "Gem::Specification.new { |s| s.name = \"other\" }")
284
+
285
+ lambda { GemKit::Release::Project.detect(dir) }
286
+ .should.raise(GemKit::Release::Project::Ambiguous)
287
+ end
288
+ end
289
+
290
+ it "reports a named gem that is not here" do
291
+ with_project.call(name: "demo") do |dir|
292
+ lambda { GemKit::Release::Project.detect(dir, name: "nope") }
293
+ .should.raise(GemKit::Release::Project::NotFound)
294
+ end
295
+ end
296
+
180
297
  it "takes overrides from the config" do
181
298
  with_project.call do |dir|
182
299
  config = GemKit::Release::Project::Config.new(changelog: "HISTORY.md", test_command: "rake")
@@ -1,19 +1,18 @@
1
1
  # Deprecations
2
2
 
3
- A deprecation in <%= name %> is a **dated promise**: it names the replacement
3
+ A deprecation in <%= subject %> is a **dated promise**: it names the replacement
4
4
  *and* the version the old name stops existing in. That promise is
5
5
  machine-readable — every declaration registers itself, and the release tooling
6
6
  refuses to ship a version that breaks one.
7
7
 
8
- The mechanism is [`GemKit::Release::Deprecate`](https://rubygems.org/gems/gem_kit-release),
8
+ The mechanism is [`GemKit::Deprecate`](https://rubygems.org/gems/gem_kit),
9
9
  built on [`Gem::Deprecate`](https://docs.ruby-lang.org/en/master/Gem/Deprecate.html).
10
10
 
11
11
  ## The rules
12
12
 
13
13
  1. **Never delete a public name outright.** Leave it working, deprecated, until
14
14
  its deadline.
15
- 2. **Every deprecation names a removal version.** The current version is
16
- <%= version %>, so the usual deadline is <%= next_major %>.
15
+ 2. **Every deprecation names a removal version.** <%= deadline_rule %>
17
16
  3. **Removals happen in major versions only.** A minor or patch release never
18
17
  takes a name away.
19
18
  4. **The deadline is enforced, not remembered.** `gem kit bump` and `gem kit release`
@@ -25,7 +24,7 @@ built on [`Gem::Deprecate`](https://docs.ruby-lang.org/en/master/Gem/Deprecate.h
25
24
 
26
25
  ```ruby
27
26
  class Session
28
- extend GemKit::Release::Deprecate
27
+ extend GemKit::Deprecate
29
28
 
30
29
  def new_reset
31
30
  # ...
@@ -56,7 +55,7 @@ against the class, not its singleton:
56
55
 
57
56
  ```ruby
58
57
  class << self
59
- extend GemKit::Release::Deprecate
58
+ extend GemKit::Deprecate
60
59
  deprecate :some_class_method, "Other.method", "<%= next_major %>"
61
60
  end
62
61
  ```
@@ -69,7 +68,7 @@ its body:
69
68
  ```ruby
70
69
  module Old
71
70
  class Thing < New::Thing
72
- extend GemKit::Release::Deprecate
71
+ extend GemKit::Deprecate
73
72
  superseded_by "New::Thing", "<%= next_major %>"
74
73
  end
75
74
  end
@@ -98,15 +97,15 @@ Pass a version to ask "what comes due here?" — it exits non-zero if anything
98
97
  does, which is what makes it usable as a gate in CI:
99
98
 
100
99
  ```sh
101
- gem kit deprecations <%= next_major %>.0.0
100
+ gem kit deprecations <%= next_major %>.0
102
101
  ```
103
102
 
104
103
  Programmatically, the same data:
105
104
 
106
105
  ```ruby
107
- GemKit::Release::Deprecate.registry # every declaration
108
- GemKit::Release::Deprecate.pending("<%= next_major %>.0.0") # deadlines that have arrived
109
- GemKit::Release::Deprecate.upcoming("<%= next_major %>.0.0") # still in their grace period
106
+ GemKit::Deprecate.registry # every declaration
107
+ GemKit::Deprecate.pending("<%= next_major %>.0") # deadlines that have arrived
108
+ GemKit::Deprecate.upcoming("<%= next_major %>.0") # still in their grace period
110
109
  ```
111
110
 
112
111
  Each entry carries `name`, `replacement`, `removed_in` and `declared_at`.
@@ -118,7 +117,7 @@ code is actually gone:
118
117
 
119
118
  ```
120
119
  $ gem kit bump major
121
- ERROR: Refusing to bump <%= version %> -> <%= next_major %>.0.0:
120
+ ERROR: Refusing to bump <%= version %> -> <%= next_major %>.0:
122
121
 
123
122
  <%= next_major %> Session#old_reset -> Session#new_reset
124
123
  lib/session.rb:19
@@ -128,11 +127,11 @@ ERROR: Refusing to bump <%= version %> -> <%= next_major %>.0.0:
128
127
 
129
128
  So the order of work is:
130
129
 
131
- 1. `gem kit deprecations <%= next_major %>.0.0` — read the list.
130
+ 1. `gem kit deprecations <%= next_major %>.0` — read the list.
132
131
  2. Delete each deprecated name and its specs. For a constant shim, that means
133
132
  deleting the whole file.
134
133
  3. Update anything in `examples/` and the docs still using the old name.
135
- 4. Record the removals in `<%= changelog %>` under `### Removed`.
134
+ 4. Record the removals in <%= changelog_reference %> under `### Removed`.
136
135
  5. `gem kit bump major` — now it goes through.
137
136
 
138
137
  `--force` exists for the case where you have decided to extend a grace period,
@@ -156,18 +155,18 @@ through:
156
155
 
157
156
  ```ruby
158
157
  captured = []
159
- original = GemKit::Release::Deprecate.method(:warn)
160
- GemKit::Release::Deprecate.define_singleton_method(:warn) { |message| captured << message }
158
+ original = GemKit::Deprecate.method(:warn)
159
+ GemKit::Deprecate.define_singleton_method(:warn) { |message| captured << message }
161
160
  begin
162
161
  legacy.old_reset
163
162
  ensure
164
- GemKit::Release::Deprecate.define_singleton_method(:warn, original)
163
+ GemKit::Deprecate.define_singleton_method(:warn, original)
165
164
  end
166
165
  ```
167
166
 
168
167
  ## See also
169
168
 
170
- - [RELEASE.md](RELEASE.md) — where the deprecation gates sit in the release
169
+ - <%= release_document_link %> — where the deprecation gates sit in the release
171
170
  process.
172
- - [<%= changelog %>](<%= changelog %>) the `### Deprecated` and `### Removed`
173
- sections are the user-facing half of all this.
171
+ - The changelog — its `### Deprecated` and `### Removed` sections are the
172
+ user-facing half of all this.