gemchain 0.2.1 → 0.2.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/CHANGELOG.md +20 -0
- data/lib/gemchain/cascade.rb +6 -4
- data/lib/gemchain/executor.rb +40 -3
- data/lib/gemchain/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ab27d10beec845df41578b7b6c4d565b7bbc0f9cba1e6a54a0dea436e6e4a6b6
|
|
4
|
+
data.tar.gz: f174ef1730a64b7ccde749d7bd501718c9e01cbdb45811c364d7c3ee3ad69f65
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6496d418a3d2678a3c68235f2dba7b6e92e6f19e43f9b419b3a84274f2aea7d4dc630255957dbff5742fab85b15aaffc9a53851801d71104ae9efd26f9224279
|
|
7
|
+
data.tar.gz: aab0e316ac54d912ae20c37ed75da0520d6a8a00deb9969e553de4286c855db40fdf399968b76141b3b3e1f9ceeec65c0bfc963f4f07fba368e8c2cac496ca20
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## [0.2.3] — 2026-08-13
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
|
|
5
|
+
- **Release step is idempotent for already-published versions** — a re-run
|
|
6
|
+
(or a flaky RubyGems API check during mode detection) reached `:release`
|
|
7
|
+
with the version already published and collided on `git tag`. The release
|
|
8
|
+
now checks RubyGems first (with a 5s timeout) and skips build/push/tag
|
|
9
|
+
when the version is already published.
|
|
10
|
+
|
|
11
|
+
## [0.2.2] — 2026-08-13
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **Release skips the commit when nothing is staged** — a source gem whose
|
|
16
|
+
version bump was already committed (e.g. a fix shipped together with the
|
|
17
|
+
bump) crashed the cascade with "nothing to commit, working tree clean".
|
|
18
|
+
`:release` now checks for staged changes (`git diff --cached --quiet`)
|
|
19
|
+
and skips the commit while still building, pushing, and tagging.
|
|
20
|
+
|
|
1
21
|
# Changelog
|
|
2
22
|
|
|
3
23
|
## [0.2.1] — 2026-08-12
|
data/lib/gemchain/cascade.rb
CHANGED
|
@@ -109,13 +109,15 @@ module Gemchain
|
|
|
109
109
|
lines.join("\n")
|
|
110
110
|
end
|
|
111
111
|
|
|
112
|
-
# True when +name+ is published at +version+ on RubyGems.
|
|
113
|
-
# is unreachable, assume not published so
|
|
114
|
-
# release) is preferred — bumping an
|
|
112
|
+
# True when +name+ is published at +version+ on RubyGems. Bounded by a
|
|
113
|
+
# short timeout; when rubygems is unreachable, assume not published so
|
|
114
|
+
# the full mode (bump → test → release) is preferred — bumping an
|
|
115
|
+
# already-published version is a no-op, and the executor's release step
|
|
116
|
+
# additionally skips re-releases of published versions.
|
|
115
117
|
def released_on_rubygems?(name, version)
|
|
116
118
|
require "json"
|
|
117
119
|
require "net/http"
|
|
118
|
-
body = Net::HTTP.get(URI("https://rubygems.org/api/v1/gems/#{name}.json"))
|
|
120
|
+
body = Timeout.timeout(5) { Net::HTTP.get(URI("https://rubygems.org/api/v1/gems/#{name}.json")) }
|
|
119
121
|
JSON.parse(body)["version"] == version
|
|
120
122
|
rescue StandardError
|
|
121
123
|
false
|
data/lib/gemchain/executor.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "open3"
|
|
4
|
+
require "timeout"
|
|
4
5
|
|
|
5
6
|
module Gemchain
|
|
6
7
|
class ExecutionError < Error; end
|
|
@@ -23,20 +24,30 @@ module Gemchain
|
|
|
23
24
|
detail = out.lines.first(5).join.strip if detail.empty?
|
|
24
25
|
raise ExecutionError, "Command failed: #{command.join(' ')}#{detail.empty? ? '' : "\n#{detail}"}"
|
|
25
26
|
end
|
|
27
|
+
|
|
28
|
+
# Runs a command and reports success without raising. Used for
|
|
29
|
+
# conditional checks (e.g. "are there staged changes?").
|
|
30
|
+
def success?(command, chdir: nil)
|
|
31
|
+
_out, _err, status = Open3.capture3(*command, chdir: chdir)
|
|
32
|
+
status.success?
|
|
33
|
+
end
|
|
26
34
|
end
|
|
27
35
|
|
|
28
36
|
BUMP_LEVELS = %i[patch minor major].freeze
|
|
29
37
|
|
|
30
38
|
# @param workspace [Workspace] the gem ecosystem
|
|
31
39
|
# @param steps [Array<Hash>] the plan from Cascade#update
|
|
32
|
-
# @param runner [#run] command runner (defaults to the real one)
|
|
40
|
+
# @param runner [#run, #success?] command runner (defaults to the real one)
|
|
33
41
|
# @param confirm [Proc] per-release confirmation, called with a message;
|
|
34
42
|
# defaults to a [y/N] prompt on stdin
|
|
35
43
|
# @param yes [Boolean] skip confirmations entirely
|
|
36
44
|
# @param bump [Symbol] dependent version bump level (:patch, :minor, :major)
|
|
37
45
|
# @param output [#puts] progress output stream
|
|
46
|
+
# @param released_check [Proc] (name, version) → published on RubyGems?
|
|
47
|
+
# Used to skip releases whose version is already published; defaults
|
|
48
|
+
# to a RubyGems API lookup with a short timeout.
|
|
38
49
|
def initialize(workspace:, steps:, runner: nil, confirm: nil, yes: false,
|
|
39
|
-
bump: :patch, output: $stdout)
|
|
50
|
+
bump: :patch, output: $stdout, released_check: nil)
|
|
40
51
|
@workspace = workspace
|
|
41
52
|
@steps = steps
|
|
42
53
|
@runner = runner || Runner.new
|
|
@@ -44,6 +55,7 @@ module Gemchain
|
|
|
44
55
|
@yes = yes
|
|
45
56
|
@bump = bump.to_sym
|
|
46
57
|
@output = output
|
|
58
|
+
@released_check = released_check || method(:released_on_rubygems?)
|
|
47
59
|
raise ArgumentError, "bump must be one of #{BUMP_LEVELS.inspect}" unless BUMP_LEVELS.include?(@bump)
|
|
48
60
|
end
|
|
49
61
|
|
|
@@ -162,6 +174,14 @@ module Gemchain
|
|
|
162
174
|
name = step[:gem]
|
|
163
175
|
version = step[:version] || bump_dependent(dir, name)
|
|
164
176
|
|
|
177
|
+
# Idempotency guard: a re-run (or flaky mode detection) can reach
|
|
178
|
+
# :release with the version already published — re-pushing/tagging
|
|
179
|
+
# would fail. Skip the release entirely in that case.
|
|
180
|
+
if @released_check.call(name, version)
|
|
181
|
+
@output.puts " ⏭ #{name} #{version} already on RubyGems — skipping release"
|
|
182
|
+
return
|
|
183
|
+
end
|
|
184
|
+
|
|
165
185
|
files = release_files(dir)
|
|
166
186
|
@runner.run(["git", "add", *files], chdir: dir)
|
|
167
187
|
|
|
@@ -170,7 +190,12 @@ module Gemchain
|
|
|
170
190
|
else
|
|
171
191
|
"Bump to #{version} (gemchain)"
|
|
172
192
|
end
|
|
173
|
-
|
|
193
|
+
# Skip the commit when the release's own bump/constraint changes were
|
|
194
|
+
# already committed (e.g. a fix shipped together with the version
|
|
195
|
+
# bump) — `git commit` would fail on a clean tree.
|
|
196
|
+
unless @runner.success?(["git", "diff", "--cached", "--quiet"], chdir: dir)
|
|
197
|
+
@runner.run(["git", "commit", "-m", message], chdir: dir)
|
|
198
|
+
end
|
|
174
199
|
|
|
175
200
|
@runner.run(["gem", "build", "#{name}.gemspec"], chdir: dir)
|
|
176
201
|
@runner.run(["gem", "push", "#{name}-#{version}.gem"], chdir: dir)
|
|
@@ -193,6 +218,18 @@ module Gemchain
|
|
|
193
218
|
status.success?
|
|
194
219
|
end
|
|
195
220
|
|
|
221
|
+
# True when +name+ is published at +version+ on RubyGems. Bounded by a
|
|
222
|
+
# short timeout; on any failure assume NOT published so the release
|
|
223
|
+
# proceeds (a no-op re-push is safer than silently skipping a real one).
|
|
224
|
+
def released_on_rubygems?(name, version)
|
|
225
|
+
require "json"
|
|
226
|
+
require "net/http"
|
|
227
|
+
body = Timeout.timeout(5) { Net::HTTP.get(URI("https://rubygems.org/api/v1/gems/#{name}.json")) }
|
|
228
|
+
JSON.parse(body)["version"] == version
|
|
229
|
+
rescue StandardError
|
|
230
|
+
false
|
|
231
|
+
end
|
|
232
|
+
|
|
196
233
|
# Dependents get an automatic version bump (they're being re-released
|
|
197
234
|
# with a tightened constraint); the source gem's version comes from the
|
|
198
235
|
# :bump step.
|
data/lib/gemchain/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gemchain
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kaka Ruto
|
|
@@ -109,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '0'
|
|
111
111
|
requirements: []
|
|
112
|
-
rubygems_version: 4.0.
|
|
112
|
+
rubygems_version: 4.0.18
|
|
113
113
|
specification_version: 4
|
|
114
114
|
summary: Cascade gem updates through interdependent gem ecosystems
|
|
115
115
|
test_files: []
|