Module: GemKit::Release

Defined in:
lib/gem_kit/release.rb,
lib/gem_kit/release/cli.rb,
lib/gem_kit/release/gate.rb,
lib/gem_kit/release/project.rb,
lib/gem_kit/release/version.rb,
lib/gem_kit/release/changelog.rb,
lib/gem_kit/release/commands/tag.rb,
lib/gem_kit/release/version_file.rb,
lib/gem_kit/release/commands/bump.rb,
lib/gem_kit/release/commands/command.rb,
lib/gem_kit/release/commands/release.rb,
lib/gem_kit/release/generators/setup.rb,
lib/gem_kit/release/commands/changelog.rb,
lib/gem_kit/release/commands/deprecations.rb

Overview

Versioning, changelog and deprecation gates for Ruby gems.

The premise: a deprecation is a dated promise — it names its replacement and the version the old name stops existing in — and a release is only honest if it keeps every promise that has come due and documents what changed. Both are checkable, so neither should depend on anyone remembering.

In a Rakefile, which is the whole integration:

require "gem_kit/release/tasks"

Everything else is inferred from the project's .gemspec. Override only what cannot be:

GemKit::Release.configure do |config|
config.changelog        = "HISTORY.md"
config.test_command     = "bin/test"
config.changelog_writer = "claude"
end

Defined Under Namespace

Modules: Commands, Generators Classes: CLI, Changelog, Failure, Gate, Project, VersionFile

Constant Summary collapse

VERSION =
"0.3.1"

Class Method Summary collapse

Class Method Details

.configObject



36
37
38
# File 'lib/gem_kit/release.rb', line 36

def self.config
  @config ||= Project::Config.new
end

.configure {|config| ... } ⇒ Object

Yields:



40
41
42
43
# File 'lib/gem_kit/release.rb', line 40

def self.configure
  yield config
  config
end

.gate(dir = Dir.pwd, name: nil) ⇒ Object



51
52
53
# File 'lib/gem_kit/release.rb', line 51

def self.gate(dir = Dir.pwd, name: nil)
  Gate.new(project(dir, name: name))
end

.plugin(&block) ⇒ Object

The extension seam. A plugin gem adds commands to gem kit by reopening the CLI through this, which is a documented entry point rather than a class_eval someone reverse-engineered:

# lib/gem_kit/plugin.rb, in a gem of your own
require "gem_kit/release/cli"

GemKit::Release.plugin do
desc "lint", "Check this gem for the things gems get wrong"
def lint = GemKit::Plugin::Lint.new(options).call
end

The block is evaluated on the Thor class, so everything Thor offers is in scope: desc, long_desc, method_option, map, and register for a Thor::Group generator. Commands added this way are indistinguishable from the built-in ones — they appear in gem kit, take --gem, and get a help page.

Loading is the plugin's own business: ship a lib/rubygems_plugin.rb that requires your file, and RubyGems will load it on every gem invocation, the same way this gem gets loaded.



172
173
174
175
# File 'lib/gem_kit/release/cli.rb', line 172

def self.plugin(&block)
  CLI.class_eval(&block)
  CLI
end

.project(dir = Dir.pwd, name: nil) ⇒ Object

The project the tooling is running in. name picks one out of a repository holding several gemspecs — this one, for instance.



47
48
49
# File 'lib/gem_kit/release.rb', line 47

def self.project(dir = Dir.pwd, name: nil)
  Project.detect(dir, name: name)
end

.reset!Object

Reset configuration — for tests.



56
57
58
# File 'lib/gem_kit/release.rb', line 56

def self.reset!
  @config = nil
end

.run(arguments, out: $stdout, err: $stderr) ⇒ Object

Run one invocation. Returns an exit status rather than exiting, so the gem kit bridge can hand it to terminate_interaction and the specs can assert on it.



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/gem_kit/release/cli.rb', line 180

def self.run(arguments, out: $stdout, err: $stderr)
  CLI.start(arguments)
  0
rescue Failure, Project::NotFound, Project::Ambiguous => failure
  # NotFound and Ambiguous are rescued here rather than only in
  # Commands::Command#project, because a Project is lazy: `detect` succeeds
  # on a gemspec that exists, and the failure surfaces later, from whichever
  # command first asks for `version` or `spec`. Catching it at the entry
  # point is the only place that covers all of them.
  err.puts(failure.message)
  1
rescue SystemExit => exit_exception
  # Thor exits on an unknown command or a missing required argument.
  exit_exception.status
end