gem_kit-release 0.3.1 → 0.3.3
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/lib/gem_kit/release/cli.rb +53 -0
- data/lib/gem_kit/release/gate.rb +3 -0
- data/lib/gem_kit/release/project.rb +168 -10
- data/lib/gem_kit/release/version.rb +1 -1
- 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: '028c5393e0908e49e47886256eb800f21382125f148bcd378bc6d2214fac27c2'
|
|
4
|
+
data.tar.gz: 85cd0ed8b03d81d4efcf0c63579b0046e273eb52eb36f9af36ab09fa8c972de8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d5d1107714f5da3a524c06a40eac52b782c71d92a0ca90972c43c5e46c9bfd53bc6bd3cc6490bd3af43ed394eec825787ab30031b05bec192dcaa967b679bdd1
|
|
7
|
+
data.tar.gz: a890866fbb74226735ed5852d2b8fab0accf513cbe529b5ff505fbde0e0f7657f5d30e500088cbd602378fbf266c6f2277dbac8f7cb80c1cf4ce669fc39dba88
|
data/lib/gem_kit/release/cli.rb
CHANGED
|
@@ -38,6 +38,23 @@ module GemKit
|
|
|
38
38
|
# Each command here is a thin front for a plain object under CLI::; the
|
|
39
39
|
# work itself is in Gate, VersionFile, Changelog and Deprecate.
|
|
40
40
|
class CLI < Thor
|
|
41
|
+
# The order the commands go in — printed above the command listing so
|
|
42
|
+
# nobody has to guess when to commit or when to write the changelog. The
|
|
43
|
+
# question the process is easy to get wrong on is exactly the one this
|
|
44
|
+
# answers: the changelog is written *after* the bump, then the two are
|
|
45
|
+
# committed together, and only then does release run.
|
|
46
|
+
WORKFLOW = <<~TXT
|
|
47
|
+
Release, in order:
|
|
48
|
+
|
|
49
|
+
1. bin/test # green suite
|
|
50
|
+
2. gem kit bump [SEGMENT] # move the version, relock the Gemfile
|
|
51
|
+
3. gem kit changelog --write # AI CLI drafts this version's entry
|
|
52
|
+
4. gem kit changelog [VERSION] # lint the entry, confirm ready to release
|
|
53
|
+
5. git commit -am "Release ..." # bump + lockfile + entry, one commit
|
|
54
|
+
6. gem kit release # gate, build, push, tag (also pushes the tag)
|
|
55
|
+
7. git push # publish the release commit
|
|
56
|
+
TXT
|
|
57
|
+
|
|
41
58
|
class_option :gem, type: :string,
|
|
42
59
|
desc: "Which gem, in a repository holding more than one gemspec"
|
|
43
60
|
|
|
@@ -49,6 +66,15 @@ module GemKit
|
|
|
49
66
|
"gem kit #{command.usage}"
|
|
50
67
|
end
|
|
51
68
|
|
|
69
|
+
# Prepend the workflow to the top-level `gem kit` help. `gem kit help
|
|
70
|
+
# <command>` sets subcommand=true and lands here too; that page already
|
|
71
|
+
# has its own long_desc, so leave it alone.
|
|
72
|
+
def self.help(shell, subcommand = false)
|
|
73
|
+
shell.say(WORKFLOW) unless subcommand
|
|
74
|
+
shell.say unless subcommand
|
|
75
|
+
super
|
|
76
|
+
end
|
|
77
|
+
|
|
52
78
|
# Short forms for the three typed most often.
|
|
53
79
|
map "log" => :changelog,
|
|
54
80
|
"deps" => :deprecations,
|
|
@@ -210,6 +236,33 @@ describe "gem_kit/release/cli" do
|
|
|
210
236
|
end
|
|
211
237
|
end
|
|
212
238
|
|
|
239
|
+
# The order between `bump` and `release` — when to write the changelog and
|
|
240
|
+
# when to commit — is exactly the question the numbered list answers, so
|
|
241
|
+
# the top-level help has to print it.
|
|
242
|
+
it "prints the numbered workflow above the command listing" do
|
|
243
|
+
with_gem do |dir|
|
|
244
|
+
_status, out, _err = invoke([], dir)
|
|
245
|
+
|
|
246
|
+
out.should.match(/Release, in order:/)
|
|
247
|
+
out.should.match(/1\. bin\/test/)
|
|
248
|
+
out.should.match(/2\. gem kit bump/)
|
|
249
|
+
out.should.match(/5\. git commit/)
|
|
250
|
+
out.should.match(/6\. gem kit release/)
|
|
251
|
+
out.should.match(/7\. git push/)
|
|
252
|
+
# The workflow lands above "Commands:", not swallowed by it.
|
|
253
|
+
out.index("Release, in order:").should < out.index("Commands:")
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# `gem kit help <command>` already has its own long_desc; the workflow
|
|
258
|
+
# belongs on the top-level page only.
|
|
259
|
+
it "does not repeat the workflow on a per-command help page" do
|
|
260
|
+
with_gem do |dir|
|
|
261
|
+
_status, out, _err = invoke(["help", "bump"], dir)
|
|
262
|
+
out.should.not.match(/Release, in order:/)
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
213
266
|
it "names the commands `gem kit <command>`, not the running program" do
|
|
214
267
|
with_gem do |dir|
|
|
215
268
|
_status, out, _err = invoke([], dir)
|
data/lib/gem_kit/release/gate.rb
CHANGED
|
@@ -98,6 +98,9 @@ __END__
|
|
|
98
98
|
|
|
99
99
|
describe "gem_kit/release/gate" do
|
|
100
100
|
require "tmpdir"
|
|
101
|
+
# These specs ask the gate whether a temp directory is a repository, so the
|
|
102
|
+
# walk up out of Dir.tmpdir has to be stopped or the answer is the machine's.
|
|
103
|
+
require_relative "../../../spec/support/git_isolation"
|
|
101
104
|
|
|
102
105
|
# A Project stub: the Gate only asks it for a changelog path and a version,
|
|
103
106
|
# and to load the library (a no-op here — the specs drive the registry).
|
|
@@ -129,7 +129,10 @@ module GemKit
|
|
|
129
129
|
# lib/gem_kit/release/version.rb for "gem_kit-release", lib/brute/version.rb
|
|
130
130
|
# for "brute" — the convention every `bundle gem` project follows.
|
|
131
131
|
def version_file
|
|
132
|
-
File.expand_path(
|
|
132
|
+
File.expand_path(
|
|
133
|
+
config.version_file || File.join("lib", *library_segments, "version.rb"),
|
|
134
|
+
root,
|
|
135
|
+
)
|
|
133
136
|
end
|
|
134
137
|
|
|
135
138
|
# An ERB template beside the version file, if the project generates it.
|
|
@@ -141,7 +144,35 @@ module GemKit
|
|
|
141
144
|
# What to require so that deprecation declarations register themselves.
|
|
142
145
|
# "gem_kit-release" -> "gem_kit/release".
|
|
143
146
|
def require_path
|
|
144
|
-
config.require_path ||
|
|
147
|
+
config.require_path || library_segments.join("/")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Where the library sits under lib/, as path segments.
|
|
151
|
+
#
|
|
152
|
+
# `bundle gem gem_kit-release` NESTS, treating the hyphen as a directory
|
|
153
|
+
# separator: lib/gem_kit/release.rb, module GemKit::Release. That is the
|
|
154
|
+
# common case and the one assumed here first.
|
|
155
|
+
#
|
|
156
|
+
# But a hyphenated name whose library is a SINGLE module does not nest.
|
|
157
|
+
# `ag-ui` is lib/ag_ui.rb holding `module AgUi` — hyphen as underscore —
|
|
158
|
+
# and the nested form names lib/ag/ui.rb, a file that was never there.
|
|
159
|
+
#
|
|
160
|
+
# A third belongs to bindings. `oo7-rb` and `sqlite3-ruby` are a library
|
|
161
|
+
# name wearing a suffix that says which language binds it, and the library
|
|
162
|
+
# underneath drops it: lib/oo7.rb holding `module Oo7`, with
|
|
163
|
+
# lib/oo7/version.rb beside it. Often the suffix is only there because
|
|
164
|
+
# RubyGems would not give up the bare name. The nested form names
|
|
165
|
+
# lib/oo7/rb.rb and the flat one lib/oo7_rb.rb, neither of which was ever
|
|
166
|
+
# there, so without this a binding gem cannot be bumped at all.
|
|
167
|
+
#
|
|
168
|
+
# All three are conventional and the name alone cannot tell them apart, so
|
|
169
|
+
# ask the tree: whichever exists is what this project is.
|
|
170
|
+
#
|
|
171
|
+
# When none exists the nested form wins, because that is what
|
|
172
|
+
# `bundle gem` would have produced and a project with no library yet is
|
|
173
|
+
# about to become one.
|
|
174
|
+
def library_segments
|
|
175
|
+
layouts.find { |segments| exists_under_lib?(segments) } || nested_layout
|
|
145
176
|
end
|
|
146
177
|
|
|
147
178
|
# Load the library, so Deprecate's registry reflects this project.
|
|
@@ -158,6 +189,40 @@ module GemKit
|
|
|
158
189
|
def test_command = config.test_command || "bin/test"
|
|
159
190
|
|
|
160
191
|
def changelog_writer = config.changelog_writer || "claude"
|
|
192
|
+
|
|
193
|
+
private
|
|
194
|
+
|
|
195
|
+
# The suffixes a binding gem wears, which its library drops.
|
|
196
|
+
BINDING_SUFFIXES = %w[rb ruby].freeze
|
|
197
|
+
|
|
198
|
+
# What a hyphenated name could mean, most conventional first. A name with
|
|
199
|
+
# no hyphen in it has exactly one meaning and the list says so.
|
|
200
|
+
def layouts
|
|
201
|
+
[nested_layout, flat_layout, *binding_layout].uniq
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def nested_layout = name.split("-")
|
|
205
|
+
|
|
206
|
+
def flat_layout = [name.tr("-", "_")]
|
|
207
|
+
|
|
208
|
+
# [["oo7"]] for "oo7-rb"; nothing for a name wearing no binding suffix,
|
|
209
|
+
# and nothing for one that is only the suffix.
|
|
210
|
+
def binding_layout
|
|
211
|
+
nested_layout.then do |segments|
|
|
212
|
+
if segments.length > 1 && BINDING_SUFFIXES.include?(segments.last)
|
|
213
|
+
[segments[0..-2]]
|
|
214
|
+
else
|
|
215
|
+
[]
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# A layout is present if the entry file is there, or the directory it
|
|
221
|
+
# would hold — a gem is free to ship lib/foo/ without lib/foo.rb.
|
|
222
|
+
def exists_under_lib?(segments)
|
|
223
|
+
base = File.join(root, "lib", *segments)
|
|
224
|
+
File.exist?("#{base}.rb") || File.directory?(base)
|
|
225
|
+
end
|
|
161
226
|
end
|
|
162
227
|
end
|
|
163
228
|
end
|
|
@@ -167,16 +232,29 @@ __END__
|
|
|
167
232
|
describe "gem_kit/release/project" do
|
|
168
233
|
require "tmpdir"
|
|
169
234
|
|
|
170
|
-
# A throwaway gem
|
|
171
|
-
|
|
235
|
+
# A throwaway gem. `layout:` picks between the three conventions a hyphenated
|
|
236
|
+
# name can follow: :nested is `bundle gem`'s lib/gem_kit/release, :flat is a
|
|
237
|
+
# single-module gem's lib/ag_ui, and :binding is a bindings gem's lib/oo7 for
|
|
238
|
+
# a gem called oo7-rb.
|
|
239
|
+
with_project = lambda do |name: "demo", version: "1.2.3", layout: :nested, &block|
|
|
172
240
|
Dir.mktmpdir do |dir|
|
|
173
|
-
|
|
241
|
+
case layout
|
|
242
|
+
when :flat then path = [name.tr("-", "_")]
|
|
243
|
+
when :binding then path = name.split("-")[0..-2]
|
|
244
|
+
else path = name.split("-")
|
|
245
|
+
end
|
|
174
246
|
FileUtils.mkdir_p(File.join(dir, "lib", *path))
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
247
|
+
|
|
248
|
+
# Nested blocks rather than `module A::B`, which is only valid once A
|
|
249
|
+
# exists -- and a throwaway gem's outermost constant never does. The one
|
|
250
|
+
# hyphenated name that got away with it was gem_kit-release, because this
|
|
251
|
+
# process has GemKit defined already.
|
|
252
|
+
constants = path.map { |segment| segment.split("_").map(&:capitalize).join }
|
|
253
|
+
lines = constants.each_with_index.map { |constant, depth| "#{" " * depth}module #{constant}" }
|
|
254
|
+
lines << "#{" " * constants.length}VERSION = #{version.inspect}"
|
|
255
|
+
lines.concat(Array.new(constants.length) { |depth| "#{" " * (constants.length - 1 - depth)}end" })
|
|
256
|
+
|
|
257
|
+
File.write(File.join(dir, "lib", *path, "version.rb"), "#{lines.join("\n")}\n")
|
|
180
258
|
File.write(File.join(dir, "#{name}.gemspec"), <<~RUBY)
|
|
181
259
|
require_relative "lib/#{path.join("/")}/version"
|
|
182
260
|
Gem::Specification.new do |spec|
|
|
@@ -218,6 +296,86 @@ describe "gem_kit/release/project" do
|
|
|
218
296
|
end
|
|
219
297
|
end
|
|
220
298
|
|
|
299
|
+
# A hyphenated name whose library is one module: lib/ag_ui.rb, not
|
|
300
|
+
# lib/ag/ui.rb. Deriving the path from the name alone named a file that was
|
|
301
|
+
# never there, so `gem kit bump` could not find the version and deprecations
|
|
302
|
+
# went undetected.
|
|
303
|
+
it "reads an underscored single-module layout off the tree" do
|
|
304
|
+
with_project.call(name: "ag-ui", layout: :flat) do |dir|
|
|
305
|
+
project = GemKit::Release::Project.detect(dir)
|
|
306
|
+
project.require_path.should == "ag_ui"
|
|
307
|
+
project.version_file.should == File.join(dir, "lib/ag_ui/version.rb")
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# A bindings gem drops the suffix: oo7-rb is lib/oo7.rb and lib/oo7/version.rb,
|
|
312
|
+
# which neither of the other two layouts names. Found the hard way -- the gem
|
|
313
|
+
# was called oo7-rb only because RubyGems holds `oo7` for something else, and
|
|
314
|
+
# `gem kit bump` then wrote to lib/oo7/rb/version.rb, a path nothing had.
|
|
315
|
+
it "reads a bindings layout, whose library drops the suffix, off the tree" do
|
|
316
|
+
with_project.call(name: "oo7-rb", layout: :binding) do |dir|
|
|
317
|
+
project = GemKit::Release::Project.detect(dir)
|
|
318
|
+
project.require_path.should == "oo7"
|
|
319
|
+
project.version_file.should == File.join(dir, "lib/oo7/version.rb")
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
it "reads a -ruby suffix the same way" do
|
|
324
|
+
with_project.call(name: "sqlite3-ruby", layout: :binding) do |dir|
|
|
325
|
+
project = GemKit::Release::Project.detect(dir)
|
|
326
|
+
project.version_file.should == File.join(dir, "lib/sqlite3/version.rb")
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# The suffix is only dropped when the tree says so. A gem that genuinely nests
|
|
331
|
+
# under a segment called "rb" keeps it.
|
|
332
|
+
it "keeps a nested layout that really does end in rb" do
|
|
333
|
+
with_project.call(name: "thing-rb", layout: :nested) do |dir|
|
|
334
|
+
project = GemKit::Release::Project.detect(dir)
|
|
335
|
+
project.require_path.should == "thing/rb"
|
|
336
|
+
project.version_file.should == File.join(dir, "lib/thing/rb/version.rb")
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# "release" is not a binding suffix, so a name ending in something else is
|
|
341
|
+
# untouched by any of this.
|
|
342
|
+
it "leaves a hyphenated name that wears no binding suffix alone" do
|
|
343
|
+
with_project.call(name: "gem_kit-release", layout: :nested) do |dir|
|
|
344
|
+
project = GemKit::Release::Project.detect(dir)
|
|
345
|
+
project.require_path.should == "gem_kit/release"
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Nesting stays the default: it is what `bundle gem` produces, so it is what
|
|
350
|
+
# a project with no library yet is about to become.
|
|
351
|
+
it "falls back to the nested layout when neither is on disk" do
|
|
352
|
+
Dir.mktmpdir do |dir|
|
|
353
|
+
File.write(File.join(dir, "ag-ui.gemspec"), <<~RUBY)
|
|
354
|
+
Gem::Specification.new do |spec|
|
|
355
|
+
spec.name = "ag-ui"
|
|
356
|
+
spec.version = "0.1.0"
|
|
357
|
+
spec.authors = ["x"]
|
|
358
|
+
spec.summary = "x"
|
|
359
|
+
spec.files = []
|
|
360
|
+
end
|
|
361
|
+
RUBY
|
|
362
|
+
GemKit::Release::Project.detect(dir).require_path.should == "ag/ui"
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# An explicit setting still wins over anything read off the tree.
|
|
367
|
+
it "prefers a configured require path and version file" do
|
|
368
|
+
with_project.call(name: "ag-ui", layout: :flat) do |dir|
|
|
369
|
+
config = GemKit::Release::Project::Config.new(
|
|
370
|
+
require_path: "somewhere/else",
|
|
371
|
+
version_file: "lib/custom.rb",
|
|
372
|
+
)
|
|
373
|
+
project = GemKit::Release::Project.detect(dir, config: config)
|
|
374
|
+
project.require_path.should == "somewhere/else"
|
|
375
|
+
project.version_file.should == File.join(dir, "lib/custom.rb")
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
221
379
|
it "defaults the changelog to CHANGELOG.md in the project root" do
|
|
222
380
|
with_project.call do |dir|
|
|
223
381
|
project = GemKit::Release::Project.detect(dir)
|