gemchain 0.2.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f57089a36952e2778e0ca90654ee97a6484ba1f6f9c002a91c1a30089c6973a
4
- data.tar.gz: cd1f24b7232ba48c8f1135d8e80374eb656e49e3322d3773b1f3680020761160
3
+ metadata.gz: ab27d10beec845df41578b7b6c4d565b7bbc0f9cba1e6a54a0dea436e6e4a6b6
4
+ data.tar.gz: f174ef1730a64b7ccde749d7bd501718c9e01cbdb45811c364d7c3ee3ad69f65
5
5
  SHA512:
6
- metadata.gz: 989b65d9e09fc06ea8445c7137b5e641fd001f7ab332023132a075c91e3a1d7b9649d34470fe78650d2c258507e5f2729d253824d85ec38b082b2efd7121e829
7
- data.tar.gz: 65efa9a75881d8880d950c2b880816e10d575c9cbf728f427a0679f5fc6fc77c37f41d836a44d5359dbd412b0d8e6bf18db25772c2cae317d37b53dc58dcef1b
6
+ metadata.gz: 6496d418a3d2678a3c68235f2dba7b6e92e6f19e43f9b419b3a84274f2aea7d4dc630255957dbff5742fab85b15aaffc9a53851801d71104ae9efd26f9224279
7
+ data.tar.gz: aab0e316ac54d912ae20c37ed75da0520d6a8a00deb9969e553de4286c855db40fdf399968b76141b3b3e1f9ceeec65c0bfc963f4f07fba368e8c2cac496ca20
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
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
+
1
11
  ## [0.2.2] — 2026-08-13
2
12
 
3
13
  ### Fixed
@@ -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. When rubygems
113
- # is unreachable, assume not published so the full mode (bump → test →
114
- # release) is preferred — bumping an already-published version is a no-op.
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
@@ -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
@@ -36,14 +37,17 @@ module Gemchain
36
37
 
37
38
  # @param workspace [Workspace] the gem ecosystem
38
39
  # @param steps [Array<Hash>] the plan from Cascade#update
39
- # @param runner [#run] command runner (defaults to the real one)
40
+ # @param runner [#run, #success?] command runner (defaults to the real one)
40
41
  # @param confirm [Proc] per-release confirmation, called with a message;
41
42
  # defaults to a [y/N] prompt on stdin
42
43
  # @param yes [Boolean] skip confirmations entirely
43
44
  # @param bump [Symbol] dependent version bump level (:patch, :minor, :major)
44
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.
45
49
  def initialize(workspace:, steps:, runner: nil, confirm: nil, yes: false,
46
- bump: :patch, output: $stdout)
50
+ bump: :patch, output: $stdout, released_check: nil)
47
51
  @workspace = workspace
48
52
  @steps = steps
49
53
  @runner = runner || Runner.new
@@ -51,6 +55,7 @@ module Gemchain
51
55
  @yes = yes
52
56
  @bump = bump.to_sym
53
57
  @output = output
58
+ @released_check = released_check || method(:released_on_rubygems?)
54
59
  raise ArgumentError, "bump must be one of #{BUMP_LEVELS.inspect}" unless BUMP_LEVELS.include?(@bump)
55
60
  end
56
61
 
@@ -169,6 +174,14 @@ module Gemchain
169
174
  name = step[:gem]
170
175
  version = step[:version] || bump_dependent(dir, name)
171
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
+
172
185
  files = release_files(dir)
173
186
  @runner.run(["git", "add", *files], chdir: dir)
174
187
 
@@ -205,6 +218,18 @@ module Gemchain
205
218
  status.success?
206
219
  end
207
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
+
208
233
  # Dependents get an automatic version bump (they're being re-released
209
234
  # with a tightened constraint); the source gem's version comes from the
210
235
  # :bump step.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gemchain
4
- VERSION = "0.2.2"
4
+ VERSION = "0.2.3"
5
5
  end
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.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto