gem_kit 0.1.1 → 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.
- checksums.yaml +4 -4
- data/README.md +186 -10
- data/lib/gem_kit/deprecate.rb +283 -0
- data/lib/gem_kit/version.rb +1 -1
- data/lib/gem_kit.rb +20 -0
- metadata +23 -77
- data/.envrc +0 -1
- data/.gitignore +0 -7
- data/Gemfile +0 -5
- data/Rakefile +0 -11
- data/bin/choose-license +0 -35
- data/bin/console +0 -8
- data/bin/increment-version +0 -44
- data/bin/release-gem +0 -32
- data/bin/rename-gem +0 -104
- data/bin/setup +0 -5
- data/bin/tag-version +0 -19
- data/bin/test +0 -14
- data/bin/update-spec +0 -49
- data/exe/gem_kit +0 -6
- data/flake.lock +0 -61
- data/flake.nix +0 -37
- data/gem_kit.gemspec +0 -34
- data/gem_kit.gemspec.erb +0 -34
- data/lib/gem_kit/version.rb.erb +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f7e45b6e07e8d99724e3bff85fdbc2d810a0fed2d4df6b31479f8df07741256
|
|
4
|
+
data.tar.gz: 96592d7cc445bc734f2c7645605360ee658e578916a5e3b957fb6984b528f5ab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 15fea17b107de0fdb5c22e9c30c594689cb88b2fd91b0e47f1987694e82f5aa9725c168a113e0f020061c2cccddd81e83bc5d785d85fb90ea6e41e8df9525ce2
|
|
7
|
+
data.tar.gz: 491a1a5290db36b81ef45d51090ae0f8707c517283c72a268887e3b573344d5bce52ea3abc6b902d73341c0acca7237437586631b088cb84e10dd980d0d58f5a
|
data/README.md
CHANGED
|
@@ -1,15 +1,191 @@
|
|
|
1
1
|
# gem_kit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Two gems, split along the line that matters: **what a library needs while it is
|
|
4
|
+
running**, and **what its maintainer needs while releasing it**.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
| Gem | Half | Depends on |
|
|
7
|
+
| --- | --- | --- |
|
|
8
|
+
| [`gem_kit`](gem_kit.gemspec) | `GemKit::Deprecate` — declare a deprecation | nothing beyond RubyGems |
|
|
9
|
+
| [`gem_kit-release`](gem_kit-release.gemspec) | `gem kit …` — enforce it at release time | `gem_kit`, `thor` |
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
A deprecation is a **dated promise**: it names its replacement *and* the version
|
|
12
|
+
the old name stops existing in. The promise is declared at runtime and enforced
|
|
13
|
+
at release time — which is exactly why these are two gems. A library that
|
|
14
|
+
deprecates a name should not thereby acquire a release toolchain, and only the
|
|
15
|
+
enforcing end needs one installed.
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
# Gemfile
|
|
19
|
+
gem "gem_kit" # runtime: declaring
|
|
20
|
+
gem "gem_kit-release", group: :development # release: enforcing
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Declaring — gem_kit
|
|
24
|
+
|
|
25
|
+
A method:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
class Session
|
|
29
|
+
extend GemKit::Deprecate
|
|
30
|
+
|
|
31
|
+
def old_reset = new_reset
|
|
32
|
+
deprecate :old_reset, "Session#new_reset", "5.0"
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
A renamed or moved constant — leave the old name as a subclass of the new one:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
class Completion < New::Completion
|
|
40
|
+
extend GemKit::Deprecate
|
|
41
|
+
superseded_by "New::Completion", "5.0"
|
|
42
|
+
end
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Both keep working, warn on use naming the caller, and register the deadline:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
NOTE: Session#old_reset is deprecated; use Session#new_reset instead.
|
|
49
|
+
It will be removed in 5.0
|
|
50
|
+
Session#old_reset called from app.rb:12.
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Use `:none` when there genuinely is no replacement. `Gem::Deprecate.skip_during`
|
|
54
|
+
silences the warnings, so a suite can exercise the old path in quiet.
|
|
55
|
+
|
|
56
|
+
## Enforcing — gem_kit-release
|
|
57
|
+
|
|
58
|
+
Installing it registers one `gem` command. There is nothing to wire up — no
|
|
59
|
+
Rakefile, no binstubs:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
gem kit setup # write DEPRECATIONS.md and RELEASE.md into your project
|
|
63
|
+
gem kit bump minor # move the version
|
|
64
|
+
gem kit changelog --write # have an AI CLI write the entry
|
|
65
|
+
gem kit changelog # lint it
|
|
66
|
+
gem kit deprecations # what is still outstanding
|
|
67
|
+
gem kit release --dry-run # run the gates
|
|
68
|
+
gem kit tag --push # tag it
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Command | What it does |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `gem kit setup` | Writes DEPRECATIONS.md and RELEASE.md, rendered for your gem's name and versions. A generator, so `--force`, `--skip` and `--pretend` all work. |
|
|
74
|
+
| `gem kit bump <major\|minor\|patch>` | Rewrites the version file. Refuses to bump onto a deprecation deadline; `--force` overrides. |
|
|
75
|
+
| `gem kit changelog [VERSION]` | Lints CHANGELOG.md. With a version, checks that version is ready to release. |
|
|
76
|
+
| `gem kit changelog --write` | Hands the entry to the configured AI CLI. |
|
|
77
|
+
| `gem kit deprecations [VERSION]` | Lists what is outstanding. With a version, fails if any come due — a CI gate. |
|
|
78
|
+
| `gem kit release [--dry-run]` | Gates, then builds and pushes. |
|
|
79
|
+
| `gem kit tag [--push]` | Tags `v<version>`, refusing if it exists. |
|
|
80
|
+
|
|
81
|
+
Everything sits behind one `gem` command rather than six, so nothing here can
|
|
82
|
+
collide with a command RubyGems ships now or adds later — `gem check`,
|
|
83
|
+
`gem build`, `gem push` and `gem setup` all already exist.
|
|
84
|
+
|
|
85
|
+
The command line is [Thor](https://github.com/rails/thor), so `gem kit` lists
|
|
86
|
+
the commands and `gem kit help <command>` prints one command's arguments and
|
|
87
|
+
options. `gem kit setup` is a `Thor::Group` generator — the same machinery
|
|
88
|
+
behind `rails generate` — which is where its `create` / `identical` /
|
|
89
|
+
`conflict` reporting and its `--force`, `--skip` and `--pretend` come from.
|
|
90
|
+
|
|
91
|
+
Everything is read out of your `.gemspec`. Override only what cannot be
|
|
92
|
+
inferred:
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
GemKit::Release.configure do |config|
|
|
96
|
+
config.changelog = "HISTORY.md" # default: CHANGELOG.md
|
|
97
|
+
config.version_file = "lib/x.rb" # default: lib/<name>/version.rb
|
|
98
|
+
config.require_path = "x" # default: <name>, hyphens as slashes
|
|
99
|
+
config.test_command = "bin/test"
|
|
100
|
+
config.changelog_writer = "claude" # the CLI that writes the entry
|
|
101
|
+
end
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### In a repository with more than one gemspec
|
|
105
|
+
|
|
106
|
+
Like this one. Every command takes `--gem`, and refuses to guess without it:
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
$ gem kit changelog
|
|
110
|
+
2 gemspecs in /home/you/src/gem_kit (gem_kit-release, gem_kit); name one with --gem
|
|
111
|
+
|
|
112
|
+
$ gem kit changelog --gem gem_kit
|
|
113
|
+
CHANGELOG-gem_kit.md is clean.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Three things follow from a repository holding several gems, and `gem kit`
|
|
117
|
+
arranges all three itself:
|
|
118
|
+
|
|
119
|
+
| | One gem | Several |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| Changelog | `CHANGELOG.md` | `CHANGELOG-<gem>.md` |
|
|
122
|
+
| Release document | `RELEASE.md` | `RELEASE-<gem>.md` |
|
|
123
|
+
| Tag | `v1.2.3` | `<gem>-v1.2.3` |
|
|
124
|
+
|
|
125
|
+
The changelog has to be per-gem: the release gate asks whether the version
|
|
126
|
+
being cut is the *topmost* released section, and two gems interleaved in one
|
|
127
|
+
file means the older one never is — so the second gem could never be released.
|
|
128
|
+
The tag has to be per-gem because `v1.2.3` says which version but not which
|
|
129
|
+
gem, which is fine until two of them are at the same one. `DEPRECATIONS.md` is
|
|
130
|
+
written once for the repository, because one deprecation policy governs
|
|
131
|
+
everything in it.
|
|
132
|
+
|
|
133
|
+
## Extending it
|
|
134
|
+
|
|
135
|
+
`gem kit` takes commands from other gems. `GemKit::Release.plugin` is the seam:
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
# lib/gem_kit/plugin.rb, in a gem of your own
|
|
139
|
+
require "gem_kit/release/cli"
|
|
140
|
+
|
|
141
|
+
GemKit::Release.plugin do
|
|
142
|
+
desc "lint", "Check this gem for the things gems get wrong"
|
|
143
|
+
def lint = GemKit::Plugin::Lint.new(options).call
|
|
144
|
+
end
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The block is evaluated on the Thor class, so the whole Thor DSL is in scope —
|
|
148
|
+
`desc`, `long_desc`, `method_option`, `map`, and `register` for a `Thor::Group`
|
|
149
|
+
generator. A command added this way is indistinguishable from a built-in one:
|
|
150
|
+
it appears in `gem kit`, takes `--gem`, and gets a help page.
|
|
151
|
+
|
|
152
|
+
Ship a `lib/rubygems_plugin.rb` that requires your file and RubyGems loads it on
|
|
153
|
+
every `gem` invocation, the same way this gem is loaded. See
|
|
154
|
+
[gem_kit-plugin](https://github.com/n-at-han-k/gem_kit-plugin) for a worked
|
|
155
|
+
example.
|
|
156
|
+
|
|
157
|
+
## Layout
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
lib/gem_kit.rb gem_kit
|
|
161
|
+
lib/gem_kit/deprecate.rb
|
|
162
|
+
lib/gem_kit/release.rb gem_kit-release
|
|
163
|
+
lib/gem_kit/release/
|
|
164
|
+
lib/rubygems_plugin.rb
|
|
165
|
+
template/ the gem template this repository used to be
|
|
15
166
|
```
|
|
167
|
+
|
|
168
|
+
One `Gemfile`, one `Gemfile.lock`, one `gemset.nix`, one `flake.nix` and one
|
|
169
|
+
`lefthook.yml` cover both gems. The dependencies are listed in the Gemfile
|
|
170
|
+
outright rather than through `gemspec`, which is the only sane answer when two
|
|
171
|
+
gemspecs share one bundle — and, separately, what `bundlerEnv` needs, since it
|
|
172
|
+
resolves against a store directory holding only a Gemfile and a lockfile.
|
|
173
|
+
|
|
174
|
+
Each gemspec names its own files rather than globbing `lib/**/*.rb`, because a
|
|
175
|
+
glob would put the whole release toolchain inside the runtime gem.
|
|
176
|
+
|
|
177
|
+
## Template
|
|
178
|
+
|
|
179
|
+
`template/` holds the gem template this repository started as: clone it, run
|
|
180
|
+
`bin/01-rename-gem`, and you have a gem.
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
direnv allow # or: nix develop
|
|
186
|
+
bin/test
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rubygems/deprecate"
|
|
4
|
+
|
|
5
|
+
module GemKit
|
|
6
|
+
# A deprecation is a dated promise: it names the replacement *and* the
|
|
7
|
+
# version the old name stops existing in. Built on Gem::Deprecate, which
|
|
8
|
+
# gets the message format and the skip_during escape hatch right, plus one
|
|
9
|
+
# addition — a registry, so the set of outstanding promises is data the
|
|
10
|
+
# release tooling can enforce rather than prose someone has to remember.
|
|
11
|
+
#
|
|
12
|
+
# Deprecate a method:
|
|
13
|
+
#
|
|
14
|
+
# class Session
|
|
15
|
+
# extend GemKit::Deprecate
|
|
16
|
+
#
|
|
17
|
+
# def old_reset = new_reset
|
|
18
|
+
# deprecate :old_reset, "Session#new_reset", "5.0"
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# Deprecate a whole constant that has moved or been renamed — leave the old
|
|
22
|
+
# name in place as a subclass of the new one, then declare it:
|
|
23
|
+
#
|
|
24
|
+
# class Completion < Brute::Completion::OpenRouter
|
|
25
|
+
# extend GemKit::Deprecate
|
|
26
|
+
# superseded_by "Brute::Completion::OpenRouter", "5.0"
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# Both warn on use, naming the caller. Gem::Deprecate.skip_during silences
|
|
30
|
+
# them, so a test suite can exercise the old path in quiet.
|
|
31
|
+
module Deprecate
|
|
32
|
+
extend Gem::Deprecate
|
|
33
|
+
|
|
34
|
+
# One outstanding deprecation. `removed_in` is the deadline the release
|
|
35
|
+
# gate reads.
|
|
36
|
+
Entry = Struct.new(:name, :replacement, :removed_in, :declared_at, keyword_init: true) do
|
|
37
|
+
def to_s
|
|
38
|
+
"#{name} -> #{replacement == :none ? "(no replacement)" : replacement}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class << self
|
|
43
|
+
# Every deprecation declared in the loaded library, in declaration order.
|
|
44
|
+
def registry
|
|
45
|
+
@registry ||= []
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def register(name:, replacement:, removed_in:, declared_at: nil)
|
|
49
|
+
entry = Entry.new(
|
|
50
|
+
name: name.to_s,
|
|
51
|
+
replacement: replacement,
|
|
52
|
+
removed_in: Gem::Version.new(removed_in.to_s),
|
|
53
|
+
declared_at: declared_at || location(1),
|
|
54
|
+
)
|
|
55
|
+
registry << entry
|
|
56
|
+
entry
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# The deprecations that come due at `version` — every deadline that has
|
|
60
|
+
# arrived or passed. Releasing `version` with any of these still in the
|
|
61
|
+
# tree breaks the promise the warning made.
|
|
62
|
+
def pending(version)
|
|
63
|
+
target = Gem::Version.new(version.to_s)
|
|
64
|
+
registry.select { |entry| entry.removed_in <= target }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Deprecations still inside their grace period at `version`.
|
|
68
|
+
def upcoming(version)
|
|
69
|
+
target = Gem::Version.new(version.to_s)
|
|
70
|
+
registry.reject { |entry| entry.removed_in <= target }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Single funnel for every warning: Gem::Deprecate.skip_during works
|
|
74
|
+
# across all of them, and specs have one place to listen.
|
|
75
|
+
def warn(message)
|
|
76
|
+
Kernel.warn(message) unless Gem::Deprecate.skip
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# The Gem::Deprecate-shaped message. `origin` must be computed at the
|
|
80
|
+
# call site — one frame deeper and it names this file rather than the
|
|
81
|
+
# code that needs changing.
|
|
82
|
+
def message(target, replacement, removed_in, origin)
|
|
83
|
+
[
|
|
84
|
+
"NOTE: #{target} is deprecated",
|
|
85
|
+
replacement == :none ? " with no replacement" : "; use #{replacement} instead",
|
|
86
|
+
". It will be removed in #{removed_in}",
|
|
87
|
+
"\n#{target} called from #{origin}.",
|
|
88
|
+
].join
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def location(depth)
|
|
92
|
+
caller_locations(depth + 1, 1)&.first&.then { |l| "#{l.path}:#{l.lineno}" }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Deprecate one method. Mirrors Gem::Deprecate#rubygems_deprecate, but the
|
|
97
|
+
# deadline is explicit — a deprecation added late in a cycle usually wants
|
|
98
|
+
# the major after next, and guessing that is not the tool's business.
|
|
99
|
+
def deprecate(name, replacement, removed_in)
|
|
100
|
+
label = singleton_class? ? "#{attached_object}.#{name}" : "#{self}##{name}"
|
|
101
|
+
Deprecate.register(name: label, replacement: replacement, removed_in: removed_in,
|
|
102
|
+
declared_at: Deprecate.location(1))
|
|
103
|
+
|
|
104
|
+
class_eval do
|
|
105
|
+
old = "_deprecated_#{name}"
|
|
106
|
+
alias_method old, name
|
|
107
|
+
define_method name do |*args, &block|
|
|
108
|
+
target = is_a?(Module) ? "#{self}.#{name}" : "#{self.class}##{name}"
|
|
109
|
+
origin = Gem.location_of_caller.join(":")
|
|
110
|
+
Deprecate.warn(Deprecate.message(target, replacement, removed_in, origin))
|
|
111
|
+
send(old, *args, &block)
|
|
112
|
+
end
|
|
113
|
+
ruby2_keywords name if respond_to?(:ruby2_keywords, true)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Deprecate the constant this is called in — the renamed-or-moved case.
|
|
118
|
+
# Named `superseded_by` rather than `deprecate_constant` because Module
|
|
119
|
+
# already has a method by that name and shadowing it would be rude.
|
|
120
|
+
def superseded_by(replacement, removed_in)
|
|
121
|
+
Deprecate.register(name: name || to_s, replacement: replacement, removed_in: removed_in,
|
|
122
|
+
declared_at: Deprecate.location(1))
|
|
123
|
+
|
|
124
|
+
return unless respond_to?(:new)
|
|
125
|
+
|
|
126
|
+
define_singleton_method(:new) do |*args, **options, &block|
|
|
127
|
+
origin = Gem.location_of_caller.join(":")
|
|
128
|
+
Deprecate.warn(Deprecate.message(name || to_s, replacement, removed_in, origin))
|
|
129
|
+
super(*args, **options, &block)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
__END__
|
|
136
|
+
|
|
137
|
+
describe "gem_kit/deprecate" do
|
|
138
|
+
Deprecate = GemKit::Deprecate unless defined?(Deprecate)
|
|
139
|
+
|
|
140
|
+
captured = []
|
|
141
|
+
# Capture what Deprecate.warn emits and keep the shared registry clean —
|
|
142
|
+
# these specs declare throwaway deprecations.
|
|
143
|
+
isolated = lambda do |&block|
|
|
144
|
+
saved = Deprecate.registry.dup
|
|
145
|
+
original = Deprecate.method(:warn)
|
|
146
|
+
captured.clear
|
|
147
|
+
Deprecate.define_singleton_method(:warn) { |message| captured << message }
|
|
148
|
+
begin
|
|
149
|
+
block.call
|
|
150
|
+
ensure
|
|
151
|
+
Deprecate.define_singleton_method(:warn, original)
|
|
152
|
+
Deprecate.registry.replace(saved)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "warns on a deprecated method, naming replacement, version and caller" do
|
|
157
|
+
isolated.call do
|
|
158
|
+
klass = Class.new do
|
|
159
|
+
extend Deprecate
|
|
160
|
+
def new_name = :result
|
|
161
|
+
def old_name = new_name
|
|
162
|
+
deprecate :old_name, "Thing#new_name", "9.0"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
klass.new.old_name.should == :result # still works
|
|
166
|
+
captured.size.should == 1
|
|
167
|
+
captured.first.should.match(/is deprecated/)
|
|
168
|
+
captured.first.should.match(/use Thing#new_name instead/)
|
|
169
|
+
captured.first.should.match(/removed in 9\.0/)
|
|
170
|
+
captured.first.should.match(/called from /)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "names the caller, not the deprecation machinery" do
|
|
175
|
+
isolated.call do
|
|
176
|
+
klass = Class.new do
|
|
177
|
+
extend Deprecate
|
|
178
|
+
def old_name = :result
|
|
179
|
+
deprecate :old_name, "Thing#new_name", "9.0"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# These specs live in this file's __END__, so "the caller" is a line in
|
|
183
|
+
# deprecate.rb either way — pin the exact line to tell them apart.
|
|
184
|
+
klass.new.old_name; call_line = __LINE__
|
|
185
|
+
captured.first.should.match(/called from .*deprecate\.rb:#{call_line}\./)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "labels a class-method deprecation by the class, not its singleton" do
|
|
190
|
+
isolated.call do
|
|
191
|
+
Class.new do
|
|
192
|
+
def self.to_s = "Demo"
|
|
193
|
+
def self.old_thing = :ok
|
|
194
|
+
class << self
|
|
195
|
+
extend Deprecate
|
|
196
|
+
deprecate :old_thing, "Other.new_thing", "9.0"
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
Deprecate.registry.last.name.should == "Demo.old_thing"
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "warns on a superseded constant but keeps it working" do
|
|
205
|
+
isolated.call do
|
|
206
|
+
modern = Class.new { def initialize(x); @x = x; end; attr_reader :x }
|
|
207
|
+
legacy = Class.new(modern) do
|
|
208
|
+
extend Deprecate
|
|
209
|
+
def self.name = "Old::Name"
|
|
210
|
+
superseded_by "New::Name", "9.0"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
legacy.new(42).x.should == 42 # still works
|
|
214
|
+
captured.size.should == 1
|
|
215
|
+
captured.first.should.match(/Old::Name is deprecated; use New::Name instead/)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it "supports :none for a deprecation with no replacement" do
|
|
220
|
+
isolated.call do
|
|
221
|
+
klass = Class.new do
|
|
222
|
+
extend Deprecate
|
|
223
|
+
def gone = :ok
|
|
224
|
+
deprecate :gone, :none, "9.0"
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
klass.new.gone
|
|
228
|
+
captured.first.should.match(/with no replacement/)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
it "registers each declaration with its deadline and source" do
|
|
233
|
+
isolated.call do
|
|
234
|
+
Class.new do
|
|
235
|
+
extend Deprecate
|
|
236
|
+
def gone = nil
|
|
237
|
+
deprecate :gone, "Other#kept", "9.0"
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
entry = Deprecate.registry.last
|
|
241
|
+
entry.replacement.should == "Other#kept"
|
|
242
|
+
entry.removed_in.should == Gem::Version.new("9.0")
|
|
243
|
+
entry.declared_at.should.match(/deprecate\.rb:\d+/)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it "splits the registry into pending and upcoming at a version" do
|
|
248
|
+
isolated.call do
|
|
249
|
+
Deprecate.registry.clear
|
|
250
|
+
Deprecate.register(name: "A", replacement: "A2", removed_in: "5.0")
|
|
251
|
+
Deprecate.register(name: "B", replacement: "B2", removed_in: "6.0")
|
|
252
|
+
|
|
253
|
+
Deprecate.pending("5.0.0").map(&:name).should == ["A"]
|
|
254
|
+
Deprecate.upcoming("5.0.0").map(&:name).should == ["B"]
|
|
255
|
+
Deprecate.pending("4.9.0").should.be.empty
|
|
256
|
+
Deprecate.pending("6.1.0").map(&:name).should == ["A", "B"]
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
it "stays quiet inside Gem::Deprecate.skip_during" do
|
|
261
|
+
saved = Deprecate.registry.dup
|
|
262
|
+
begin
|
|
263
|
+
klass = Class.new do
|
|
264
|
+
extend Deprecate
|
|
265
|
+
def quiet = :ok
|
|
266
|
+
deprecate :quiet, "Other#loud", "9.0"
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
warned = []
|
|
270
|
+
original = Kernel.method(:warn)
|
|
271
|
+
Kernel.define_singleton_method(:warn) { |*args| warned << args.join }
|
|
272
|
+
begin
|
|
273
|
+
Gem::Deprecate.skip_during { klass.new.quiet.should == :ok }
|
|
274
|
+
ensure
|
|
275
|
+
Kernel.define_singleton_method(:warn, original)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
warned.should.be.empty
|
|
279
|
+
ensure
|
|
280
|
+
Deprecate.registry.replace(saved)
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
data/lib/gem_kit/version.rb
CHANGED
data/lib/gem_kit.rb
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "gem_kit/version"
|
|
4
|
+
require_relative "gem_kit/deprecate"
|
|
4
5
|
|
|
6
|
+
# The runtime half of the kit: what a library needs while it is *running*, as
|
|
7
|
+
# opposed to what its maintainer needs while releasing it.
|
|
8
|
+
#
|
|
9
|
+
# Right now that is one thing — GemKit::Deprecate, the deprecation DSL. It has
|
|
10
|
+
# no dependencies beyond RubyGems' own, deliberately: a library that deprecates
|
|
11
|
+
# a name should not thereby acquire a release toolchain.
|
|
12
|
+
#
|
|
13
|
+
# class Session
|
|
14
|
+
# extend GemKit::Deprecate
|
|
15
|
+
#
|
|
16
|
+
# def old_reset = new_reset
|
|
17
|
+
# deprecate :old_reset, "Session#new_reset", "5.0"
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# The release half lives in the gem_kit-release gem, in this same repository:
|
|
21
|
+
# `gem kit bump|changelog|deprecations|release|tag`. It reads the registry this
|
|
22
|
+
# one populates, which is the whole point of the split — the promises are
|
|
23
|
+
# declared at runtime and enforced at release time, and only the enforcing end
|
|
24
|
+
# needs the tooling installed.
|
|
5
25
|
module GemKit
|
|
6
26
|
end
|
metadata
CHANGED
|
@@ -1,96 +1,42 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gem_kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
- Nathan
|
|
8
|
-
bindir:
|
|
7
|
+
- Nathan Kidd
|
|
8
|
+
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-01 00:00:00.000000000 Z
|
|
11
|
-
dependencies:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- !ruby/object:Gem::Version
|
|
18
|
-
version: '5.0'
|
|
19
|
-
type: :development
|
|
20
|
-
prerelease: false
|
|
21
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
-
requirements:
|
|
23
|
-
- - "~>"
|
|
24
|
-
- !ruby/object:Gem::Version
|
|
25
|
-
version: '5.0'
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: rake
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - "~>"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '13.0'
|
|
33
|
-
type: :development
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - "~>"
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '13.0'
|
|
40
|
-
- !ruby/object:Gem::Dependency
|
|
41
|
-
name: rubocop
|
|
42
|
-
requirement: !ruby/object:Gem::Requirement
|
|
43
|
-
requirements:
|
|
44
|
-
- - "~>"
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: '1.21'
|
|
47
|
-
type: :development
|
|
48
|
-
prerelease: false
|
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
-
requirements:
|
|
51
|
-
- - "~>"
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: '1.21'
|
|
54
|
-
description: 'Clone the repo and run bin/rename-gem and you have a gem.
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: |
|
|
13
|
+
A deprecation is a dated promise: it names its replacement and the version
|
|
14
|
+
the old name stops existing in. GemKit::Deprecate declares that promise --
|
|
15
|
+
`deprecate` for a method, `superseded_by` for a renamed constant -- warns
|
|
16
|
+
on use naming the caller, and registers the deadline so it can be checked.
|
|
55
17
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
-
|
|
18
|
+
Built on Gem::Deprecate, so the message format and Gem::Deprecate.skip_during
|
|
19
|
+
work as they already do. No dependencies beyond RubyGems' own: a library
|
|
20
|
+
that deprecates a name should not thereby acquire a release toolchain.
|
|
21
|
+
|
|
22
|
+
The checking lives in the gem_kit-release gem, which reads this registry.
|
|
23
|
+
email: nathanblenheimkidd@gmail.com
|
|
24
|
+
executables: []
|
|
61
25
|
extensions: []
|
|
62
26
|
extra_rdoc_files: []
|
|
63
27
|
files:
|
|
64
|
-
- ".envrc"
|
|
65
|
-
- ".gitignore"
|
|
66
|
-
- Gemfile
|
|
67
28
|
- LICENSE
|
|
68
29
|
- README.md
|
|
69
|
-
- Rakefile
|
|
70
|
-
- bin/choose-license
|
|
71
|
-
- bin/console
|
|
72
|
-
- bin/increment-version
|
|
73
|
-
- bin/release-gem
|
|
74
|
-
- bin/rename-gem
|
|
75
|
-
- bin/setup
|
|
76
|
-
- bin/tag-version
|
|
77
|
-
- bin/test
|
|
78
|
-
- bin/update-spec
|
|
79
|
-
- exe/gem_kit
|
|
80
|
-
- flake.lock
|
|
81
|
-
- flake.nix
|
|
82
|
-
- gem_kit.gemspec
|
|
83
|
-
- gem_kit.gemspec.erb
|
|
84
30
|
- lib/gem_kit.rb
|
|
31
|
+
- lib/gem_kit/deprecate.rb
|
|
85
32
|
- lib/gem_kit/version.rb
|
|
86
|
-
|
|
87
|
-
homepage: https://github.com/n-at-han-k/gem-kit
|
|
33
|
+
homepage: https://github.com/n-at-han-k/gem_kit
|
|
88
34
|
licenses:
|
|
89
35
|
- MIT
|
|
90
36
|
metadata:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
37
|
+
source_code_uri: https://github.com/n-at-han-k/gem_kit
|
|
38
|
+
changelog_uri: https://github.com/n-at-han-k/gem_kit/blob/main/CHANGELOG.md
|
|
39
|
+
bug_tracker_uri: https://github.com/n-at-han-k/gem_kit/issues
|
|
94
40
|
rubygems_mfa_required: 'true'
|
|
95
41
|
rdoc_options: []
|
|
96
42
|
require_paths:
|
|
@@ -99,7 +45,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
99
45
|
requirements:
|
|
100
46
|
- - ">="
|
|
101
47
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: 3.2
|
|
48
|
+
version: '3.2'
|
|
103
49
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
50
|
requirements:
|
|
105
51
|
- - ">="
|
|
@@ -108,5 +54,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
108
54
|
requirements: []
|
|
109
55
|
rubygems_version: 3.7.2
|
|
110
56
|
specification_version: 4
|
|
111
|
-
summary:
|
|
57
|
+
summary: A deprecation DSL that makes a removal deadline enforceable
|
|
112
58
|
test_files: []
|
data/.envrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
use flake
|
data/.gitignore
DELETED
data/Gemfile
DELETED
data/Rakefile
DELETED
data/bin/choose-license
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "json"
|
|
5
|
-
require "net/http"
|
|
6
|
-
require "uri"
|
|
7
|
-
|
|
8
|
-
def fetch_json(url)
|
|
9
|
-
JSON.parse(Net::HTTP.get(URI(url)))
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
licenses = fetch_json("https://api.github.com/licenses")
|
|
13
|
-
|
|
14
|
-
licenses.each_with_index { |l, i| puts "#{i + 1}. #{l['name']}" }
|
|
15
|
-
|
|
16
|
-
print "\nPick a license: "
|
|
17
|
-
choice = licenses[gets.to_i - 1]
|
|
18
|
-
|
|
19
|
-
abort("Invalid choice") unless choice
|
|
20
|
-
|
|
21
|
-
detail = fetch_json(choice["url"])
|
|
22
|
-
|
|
23
|
-
license_path = File.expand_path("../LICENSE", __dir__)
|
|
24
|
-
File.write(license_path, detail["body"])
|
|
25
|
-
|
|
26
|
-
# Update the SPDX ID in the gemspec
|
|
27
|
-
spdx_id = choice["spdx_id"]
|
|
28
|
-
gemspec_path = Dir.glob(File.expand_path("../*.gemspec", __dir__)).first
|
|
29
|
-
if gemspec_path
|
|
30
|
-
content = File.read(gemspec_path)
|
|
31
|
-
content.sub!(/spec\.license\s*=\s*"[^"]*"/, "spec.license = \"#{spdx_id}\"")
|
|
32
|
-
File.write(gemspec_path, content)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
puts "Wrote #{spdx_id} license to LICENSE"
|
data/bin/console
DELETED
data/bin/increment-version
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "erb"
|
|
5
|
-
require_relative "../lib/gem_kit/version"
|
|
6
|
-
|
|
7
|
-
USAGE = <<~TEXT
|
|
8
|
-
Usage: bin/increment-version <major|minor|patch>
|
|
9
|
-
TEXT
|
|
10
|
-
|
|
11
|
-
segment = ARGV[0]
|
|
12
|
-
|
|
13
|
-
unless %w[major minor patch].include?(segment)
|
|
14
|
-
warn USAGE
|
|
15
|
-
exit 1
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
current = GemKit::VERSION
|
|
19
|
-
major, minor, patch = current.split(".").map(&:to_i)
|
|
20
|
-
|
|
21
|
-
case segment
|
|
22
|
-
when "major"
|
|
23
|
-
major += 1
|
|
24
|
-
minor = 0
|
|
25
|
-
patch = 0
|
|
26
|
-
when "minor"
|
|
27
|
-
minor += 1
|
|
28
|
-
patch = 0
|
|
29
|
-
when "patch"
|
|
30
|
-
patch += 1
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
version = "#{major}.#{minor}.#{patch}"
|
|
34
|
-
|
|
35
|
-
template_path = File.expand_path("../lib/gem_kit/version.rb.erb", __dir__)
|
|
36
|
-
output_path = File.expand_path("../lib/gem_kit/version.rb", __dir__)
|
|
37
|
-
|
|
38
|
-
template = ERB.new(File.read(template_path))
|
|
39
|
-
result = template.result(binding)
|
|
40
|
-
|
|
41
|
-
File.write(output_path, result)
|
|
42
|
-
|
|
43
|
-
puts "#{current} -> #{version}"
|
|
44
|
-
|
data/bin/release-gem
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require_relative "../lib/gem_kit/version"
|
|
5
|
-
|
|
6
|
-
local_version = GemKit::VERSION
|
|
7
|
-
gem_name = "gem_kit"
|
|
8
|
-
gemspec = "gem_kit.gemspec"
|
|
9
|
-
|
|
10
|
-
puts "Local version: #{local_version}"
|
|
11
|
-
|
|
12
|
-
remote_output = `gem specification #{gem_name} version --remote 2>&1`
|
|
13
|
-
|
|
14
|
-
if $?.success?
|
|
15
|
-
remote_version = remote_output[/version: (.+)/, 1]&.strip
|
|
16
|
-
puts "Remote version: #{remote_version}"
|
|
17
|
-
|
|
18
|
-
if Gem::Version.new(local_version) <= Gem::Version.new(remote_version)
|
|
19
|
-
abort "ERROR: Local version (#{local_version}) has not been incremented past remote (#{remote_version})"
|
|
20
|
-
end
|
|
21
|
-
else
|
|
22
|
-
puts "Gem not yet published remotely, proceeding with first release"
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
puts "Building #{gemspec}..."
|
|
26
|
-
system("gem build #{gemspec}") || abort("ERROR: gem build failed")
|
|
27
|
-
|
|
28
|
-
gem_file = "#{gem_name}-#{local_version}.gem"
|
|
29
|
-
puts "Pushing #{gem_file}..."
|
|
30
|
-
system("gem push #{gem_file}") || abort("ERROR: gem push failed")
|
|
31
|
-
|
|
32
|
-
puts "Released #{gem_name} #{local_version}"
|
data/bin/rename-gem
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "fileutils"
|
|
5
|
-
|
|
6
|
-
current_name = "gem_kit"
|
|
7
|
-
|
|
8
|
-
def to_camel(snake)
|
|
9
|
-
snake.split("_").map(&:capitalize).join
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Parse --from flag
|
|
13
|
-
args = ARGV.dup
|
|
14
|
-
if (idx = args.index("--from"))
|
|
15
|
-
current_name = args.delete_at(idx + 1)&.tr("-", "_")
|
|
16
|
-
args.delete_at(idx)
|
|
17
|
-
abort "ERROR: --from requires a gem name" unless current_name && !current_name.empty?
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
name = args[0]
|
|
21
|
-
|
|
22
|
-
if name.nil? || name.empty?
|
|
23
|
-
print "Gem name (snake_case): "
|
|
24
|
-
name = $stdin.gets.chomp
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
abort "Usage: bin/rename-gem <gem_name> [--from <current_name>]" if name.empty?
|
|
28
|
-
|
|
29
|
-
old_snake = current_name
|
|
30
|
-
old_camel = to_camel(old_snake)
|
|
31
|
-
|
|
32
|
-
snake = name.tr("-", "_")
|
|
33
|
-
camel = to_camel(snake)
|
|
34
|
-
|
|
35
|
-
if snake == old_snake
|
|
36
|
-
abort "Already named #{snake}, nothing to do"
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
root = File.expand_path("..", __dir__)
|
|
40
|
-
|
|
41
|
-
# --- Rename files and directories ---
|
|
42
|
-
|
|
43
|
-
renames = {
|
|
44
|
-
"lib/#{old_snake}" => "lib/#{snake}",
|
|
45
|
-
"lib/#{old_snake}.rb" => "lib/#{snake}.rb",
|
|
46
|
-
"lib/#{snake}/version.rb" => "lib/#{snake}/version.rb",
|
|
47
|
-
"lib/#{snake}/version.rb.erb" => "lib/#{snake}/version.rb.erb",
|
|
48
|
-
"#{old_snake}.gemspec" => "#{snake}.gemspec",
|
|
49
|
-
"#{old_snake}.gemspec.erb" => "#{snake}.gemspec.erb",
|
|
50
|
-
"exe/#{old_snake}" => "exe/#{snake}",
|
|
51
|
-
"test/#{old_snake}_test.rb" => "test/#{snake}_test.rb",
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
# Rename directory first (lib/old_name -> lib/new_name)
|
|
55
|
-
old_dir = File.join(root, "lib", old_snake)
|
|
56
|
-
new_dir = File.join(root, "lib", snake)
|
|
57
|
-
if File.directory?(old_dir)
|
|
58
|
-
FileUtils.mv(old_dir, new_dir)
|
|
59
|
-
puts " mv lib/#{old_snake}/ -> lib/#{snake}/"
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Rename files
|
|
63
|
-
renames.each do |old_rel, new_rel|
|
|
64
|
-
next if old_rel.start_with?("lib/#{old_snake}/") # directory already moved
|
|
65
|
-
next if old_rel == "lib/#{snake}/version.rb" # already inside renamed dir
|
|
66
|
-
|
|
67
|
-
old_path = File.join(root, old_rel)
|
|
68
|
-
new_path = File.join(root, new_rel)
|
|
69
|
-
next unless File.exist?(old_path)
|
|
70
|
-
next if old_path == new_path
|
|
71
|
-
|
|
72
|
-
FileUtils.mv(old_path, new_path)
|
|
73
|
-
puts " mv #{old_rel} -> #{new_rel}"
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# --- Replace content in all files ---
|
|
77
|
-
|
|
78
|
-
files = Dir.glob(File.join(root, "**/*"), File::FNM_DOTMATCH)
|
|
79
|
-
.reject { |f| File.directory?(f) }
|
|
80
|
-
.reject { |f| f.include?("/.git/") }
|
|
81
|
-
|
|
82
|
-
files.each do |path|
|
|
83
|
-
content = File.read(path)
|
|
84
|
-
original = content.dup
|
|
85
|
-
|
|
86
|
-
content.gsub!(old_camel, camel)
|
|
87
|
-
content.gsub!(old_snake, snake)
|
|
88
|
-
|
|
89
|
-
if content != original
|
|
90
|
-
File.write(path, content)
|
|
91
|
-
puts " updated #{path.sub("#{root}/", "")}"
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# Update current_name in this script to reflect the new name
|
|
96
|
-
script_path = File.join(root, "bin", "rename-gem")
|
|
97
|
-
if File.exist?(script_path)
|
|
98
|
-
script = File.read(script_path)
|
|
99
|
-
script.sub!(/^current_name = ".*"$/, "current_name = \"#{snake}\"")
|
|
100
|
-
File.write(script_path, script)
|
|
101
|
-
puts " updated bin/rename-gem (current_name = \"#{snake}\")"
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
puts "\nRenamed #{old_snake} -> #{snake} (#{old_camel} -> #{camel})"
|
data/bin/setup
DELETED
data/bin/tag-version
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require_relative "../lib/gem_kit/version"
|
|
5
|
-
|
|
6
|
-
version = GemKit::VERSION
|
|
7
|
-
tag = "v#{version}"
|
|
8
|
-
|
|
9
|
-
existing = `git tag -l #{tag}`.strip
|
|
10
|
-
unless existing.empty?
|
|
11
|
-
abort "ERROR: Tag #{tag} already exists"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
system("git tag #{tag}") || abort("ERROR: Failed to create tag #{tag}")
|
|
15
|
-
|
|
16
|
-
puts "Tagged #{tag}"
|
|
17
|
-
puts ""
|
|
18
|
-
puts "To push the tag, run:"
|
|
19
|
-
puts " git push --tags"
|
data/bin/test
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
$LOAD_PATH.unshift File.expand_path("../test", __dir__)
|
|
5
|
-
|
|
6
|
-
require "bundler/setup"
|
|
7
|
-
|
|
8
|
-
Dir.chdir(File.expand_path("..", __dir__))
|
|
9
|
-
|
|
10
|
-
if ARGV.empty?
|
|
11
|
-
Dir.glob("test/**/*_test.rb").sort.each { |f| require_relative "../#{f}" }
|
|
12
|
-
else
|
|
13
|
-
ARGV.each { |f| require_relative "../#{f}" }
|
|
14
|
-
end
|
data/bin/update-spec
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "erb"
|
|
5
|
-
require_relative "../lib/gem_kit/version"
|
|
6
|
-
|
|
7
|
-
gemspec_path = File.expand_path("../gem_kit.gemspec", __dir__)
|
|
8
|
-
spec = Gem::Specification.load(gemspec_path)
|
|
9
|
-
|
|
10
|
-
def prompt(label, default)
|
|
11
|
-
print "#{label} [#{default}] (y/N): "
|
|
12
|
-
return default unless $stdin.gets.chomp.downcase == "y"
|
|
13
|
-
|
|
14
|
-
print "#{label}: "
|
|
15
|
-
input = $stdin.gets.chomp
|
|
16
|
-
input.empty? ? default : input
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def prompt_script(label, current, script)
|
|
20
|
-
print "#{label} [#{current}] (y/N): "
|
|
21
|
-
return unless $stdin.gets.chomp.downcase == "y"
|
|
22
|
-
|
|
23
|
-
system(File.expand_path(script, __dir__))
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
prompt_script("Gem name", spec.name, "rename-gem")
|
|
27
|
-
authors = prompt("Authors (comma-separated)", spec.authors.join(", ")).split(",").map(&:strip)
|
|
28
|
-
emails = prompt("Emails (comma-separated)", spec.email.join(", ")).split(",").map(&:strip)
|
|
29
|
-
summary = prompt("Summary", spec.summary)
|
|
30
|
-
description = prompt("Description", spec.description.strip)
|
|
31
|
-
homepage = prompt("Homepage", spec.homepage)
|
|
32
|
-
prompt_script("License", spec.license, "choose-license")
|
|
33
|
-
ruby_version = prompt("Minimum Ruby version", spec.required_ruby_version.to_s.delete(">= "))
|
|
34
|
-
|
|
35
|
-
# Reload spec in case rename-gem or choose-license changed things
|
|
36
|
-
gemspec_path = Dir.glob(File.expand_path("../*.gemspec", __dir__)).first
|
|
37
|
-
spec = Gem::Specification.load(gemspec_path)
|
|
38
|
-
gem_name = spec.name
|
|
39
|
-
license = spec.license
|
|
40
|
-
|
|
41
|
-
template_path = gemspec_path.sub(/\.gemspec$/, ".gemspec.erb")
|
|
42
|
-
output_path = gemspec_path
|
|
43
|
-
|
|
44
|
-
template = ERB.new(File.read(template_path))
|
|
45
|
-
result = template.result(binding)
|
|
46
|
-
|
|
47
|
-
File.write(output_path, result)
|
|
48
|
-
|
|
49
|
-
puts "Updated #{File.basename(gemspec_path)}"
|
data/exe/gem_kit
DELETED
data/flake.lock
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"nodes": {
|
|
3
|
-
"nixpkgs": {
|
|
4
|
-
"locked": {
|
|
5
|
-
"lastModified": 1773821835,
|
|
6
|
-
"narHash": "sha256-TJ3lSQtW0E2JrznGVm8hOQGVpXjJyXY2guAxku2O9A4=",
|
|
7
|
-
"owner": "NixOS",
|
|
8
|
-
"repo": "nixpkgs",
|
|
9
|
-
"rev": "b40629efe5d6ec48dd1efba650c797ddbd39ace0",
|
|
10
|
-
"type": "github"
|
|
11
|
-
},
|
|
12
|
-
"original": {
|
|
13
|
-
"owner": "NixOS",
|
|
14
|
-
"ref": "nixos-unstable",
|
|
15
|
-
"repo": "nixpkgs",
|
|
16
|
-
"type": "github"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"root": {
|
|
20
|
-
"inputs": {
|
|
21
|
-
"nixpkgs": "nixpkgs",
|
|
22
|
-
"utils": "utils"
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
"systems": {
|
|
26
|
-
"locked": {
|
|
27
|
-
"lastModified": 1681028828,
|
|
28
|
-
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
29
|
-
"owner": "nix-systems",
|
|
30
|
-
"repo": "default",
|
|
31
|
-
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
32
|
-
"type": "github"
|
|
33
|
-
},
|
|
34
|
-
"original": {
|
|
35
|
-
"owner": "nix-systems",
|
|
36
|
-
"repo": "default",
|
|
37
|
-
"type": "github"
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
"utils": {
|
|
41
|
-
"inputs": {
|
|
42
|
-
"systems": "systems"
|
|
43
|
-
},
|
|
44
|
-
"locked": {
|
|
45
|
-
"lastModified": 1731533236,
|
|
46
|
-
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
|
47
|
-
"owner": "numtide",
|
|
48
|
-
"repo": "flake-utils",
|
|
49
|
-
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
50
|
-
"type": "github"
|
|
51
|
-
},
|
|
52
|
-
"original": {
|
|
53
|
-
"owner": "numtide",
|
|
54
|
-
"repo": "flake-utils",
|
|
55
|
-
"type": "github"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
"root": "root",
|
|
60
|
-
"version": 7
|
|
61
|
-
}
|
data/flake.nix
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
description = "Ruby gem flake";
|
|
3
|
-
|
|
4
|
-
inputs = {
|
|
5
|
-
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
6
|
-
utils.url = "github:numtide/flake-utils";
|
|
7
|
-
};
|
|
8
|
-
outputs = { self, nixpkgs, utils }:
|
|
9
|
-
utils.lib.eachDefaultSystem (system:
|
|
10
|
-
let
|
|
11
|
-
pkgs = nixpkgs.legacyPackages.${system};
|
|
12
|
-
ruby = pkgs.ruby_3_4; # Specify version
|
|
13
|
-
in
|
|
14
|
-
{
|
|
15
|
-
devShells.default = pkgs.mkShell {
|
|
16
|
-
nativeBuildInputs = [
|
|
17
|
-
pkgs.pkg-config # native extension discovery
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
buildInputs = [
|
|
21
|
-
ruby
|
|
22
|
-
pkgs.libyaml # psych gem
|
|
23
|
-
pkgs.openssl # openssl gem
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
shellHook = ''
|
|
27
|
-
export GEM_HOME="$PWD/.gem"
|
|
28
|
-
export GEM_PATH="$GEM_HOME"
|
|
29
|
-
export PATH="$GEM_HOME/bin:$PATH"
|
|
30
|
-
export BUNDLE_PATH="$GEM_HOME"
|
|
31
|
-
export BUNDLE_BIN="$GEM_HOME/bin"
|
|
32
|
-
'';
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
data/gem_kit.gemspec
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "lib/gem_kit/version"
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name = "gem_kit"
|
|
7
|
-
spec.version = GemKit::VERSION
|
|
8
|
-
spec.authors = ["Nathan K"]
|
|
9
|
-
spec.email = ["nathankidd@hey.com"]
|
|
10
|
-
|
|
11
|
-
spec.summary = "Dynamic gem template"
|
|
12
|
-
|
|
13
|
-
spec.description = <<~DESC
|
|
14
|
-
Clone the repo and run bin/rename-gem and you have a gem.
|
|
15
|
-
DESC
|
|
16
|
-
|
|
17
|
-
spec.homepage = "https://github.com/n-at-han-k/gem-kit"
|
|
18
|
-
spec.license = "MIT"
|
|
19
|
-
spec.required_ruby_version = ">= 3.2.0"
|
|
20
|
-
|
|
21
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
|
22
|
-
spec.metadata["source_code_uri"] = spec.homepage
|
|
23
|
-
spec.metadata["documentation_uri"] = spec.homepage
|
|
24
|
-
spec.metadata["rubygems_mfa_required"] = "true"
|
|
25
|
-
|
|
26
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|data)/}) }
|
|
27
|
-
spec.bindir = "exe"
|
|
28
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
29
|
-
spec.require_paths = ["lib"]
|
|
30
|
-
|
|
31
|
-
spec.add_development_dependency "minitest", "~> 5.0"
|
|
32
|
-
spec.add_development_dependency "rake", "~> 13.0"
|
|
33
|
-
spec.add_development_dependency "rubocop", "~> 1.21"
|
|
34
|
-
end
|
data/gem_kit.gemspec.erb
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "lib/gem_kit/version"
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name = "<%= gem_name %>"
|
|
7
|
-
spec.version = GemKit::VERSION
|
|
8
|
-
spec.authors = [<%= authors.map { |a| %("#{a}") }.join(", ") %>]
|
|
9
|
-
spec.email = [<%= emails.map { |e| %("#{e}") }.join(", ") %>]
|
|
10
|
-
|
|
11
|
-
spec.summary = "<%= summary %>"
|
|
12
|
-
|
|
13
|
-
spec.description = <<~DESC
|
|
14
|
-
<%= description %>
|
|
15
|
-
DESC
|
|
16
|
-
|
|
17
|
-
spec.homepage = "<%= homepage %>"
|
|
18
|
-
spec.license = "<%= license %>"
|
|
19
|
-
spec.required_ruby_version = ">= <%= ruby_version %>"
|
|
20
|
-
|
|
21
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
|
22
|
-
spec.metadata["source_code_uri"] = spec.homepage
|
|
23
|
-
spec.metadata["documentation_uri"] = spec.homepage
|
|
24
|
-
spec.metadata["rubygems_mfa_required"] = "true"
|
|
25
|
-
|
|
26
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|data)/}) }
|
|
27
|
-
spec.bindir = "exe"
|
|
28
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
29
|
-
spec.require_paths = ["lib"]
|
|
30
|
-
|
|
31
|
-
spec.add_development_dependency "minitest", "~> 5.0"
|
|
32
|
-
spec.add_development_dependency "rake", "~> 13.0"
|
|
33
|
-
spec.add_development_dependency "rubocop", "~> 1.21"
|
|
34
|
-
end
|