gemchain 0.2.0 → 0.2.1

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: cfa66ea15e541d38928f3f19469437e021e4e40566af6dbc68c17bdda991c986
4
- data.tar.gz: c9870c9615acd4f464302d282bf50ba722f098d5ed6e4e1da3c4ef60c46ab25e
3
+ metadata.gz: 29cc49ef5619e2bf869d99e3785600c68fce13cac87d5f455103e82ad67964df
4
+ data.tar.gz: e3ee5374417db2c3bf89028b0eb2527dd698217f850bfc9402e1c68c364aed61
5
5
  SHA512:
6
- metadata.gz: 0e4133d289e5b13d797ec17a8b57093e1cb0a4d74b5b65c865f9b7317d9e6714461e2b7d15cd873cbf0890fec66ec8cb7b1bd3c5290ebe247ffd63ea9c8bc9f4
7
- data.tar.gz: 0e00b7f943cf3ba4f39f831eebe8b128a4a5e6582d101f9b69cf46663ebfe8b639d74a858eb39921aa6d4c31eb7b336c6b8492a3aa016e15aca7389559611a70
6
+ metadata.gz: 5b37952e40c01005984baae5a1d077b62af99ab7b77f9a464578dd16a02ec33b6e4c3a371ab7bdeadd99fbbc6c1e9c590f0744d0ab80e4100aa431e5a9ab9c46
7
+ data.tar.gz: 9f9e33a3c2cf12eeb19e0eb641523dfa6e46eadccef43a79352cb4446e6e8ffd77acad93fc1d9697de9c33fa6590634cbd67c20073ff67bafe8895cac62bec96
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.1] — 2026-08-12
4
+
5
+ ### Fixed
6
+
7
+ - **Release staging skips gitignored `Gemfile.lock`** — `git add` failed for
8
+ gem repos that deliberately ignore their lockfile (e.g. ask-mcp), aborting
9
+ the cascade mid-release. Staging now skips files excluded via `.gitignore`.
10
+ - **Cascade mode detection verifies the release actually happened** — a
11
+ failed run can leave `version.rb` bumped while the gem was never pushed;
12
+ the previous `version.rb == target` check then skipped the source release
13
+ and released dependents against a nonexistent version. `update` now checks
14
+ RubyGems for the published version (injectable for tests) before assuming
15
+ post-release mode.
16
+
3
17
  ## [0.2.0] — 2026-08-10
4
18
 
5
19
  ### Added
@@ -4,8 +4,9 @@ module Gemchain
4
4
  class Cascade
5
5
  Result = Struct.new(:success, :message, :details, keyword_init: true)
6
6
 
7
- def initialize(workspace)
7
+ def initialize(workspace, released_check: nil)
8
8
  @workspace = workspace
9
+ @released_check = released_check || method(:released_on_rubygems?)
9
10
  end
10
11
 
11
12
  # Pre-release guard: warns about gems that depend on +gem_name+.
@@ -44,7 +45,9 @@ module Gemchain
44
45
  end
45
46
 
46
47
  # Performs the full cascade update:
47
- # 1. If the source gem isn't already at +new_version+: bump, test, release it
48
+ # 1. If the source gem isn't already at +new_version+ AND published on
49
+ # RubyGems: bump, test, release it (a version.rb match alone is not
50
+ # enough — a failed run can leave it bumped but unpublished)
48
51
  # 2. For each dependent (topological order): update constraint, test, bump, release
49
52
  #
50
53
  # With test_only: true, only the dependents' :test steps are emitted —
@@ -60,7 +63,7 @@ module Gemchain
60
63
  steps = []
61
64
  order = @workspace.cascade_order(gem_name)
62
65
 
63
- if !test_only && gem.version.to_s != new_version
66
+ if !test_only && (gem.version.to_s != new_version || !@released_check.call(gem_name, new_version))
64
67
  # The source must be released before dependents' `bundle update`
65
68
  # can resolve the new version from RubyGems.
66
69
  steps << {action: :bump, gem: gem_name, version: new_version}
@@ -105,5 +108,17 @@ module Gemchain
105
108
 
106
109
  lines.join("\n")
107
110
  end
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.
115
+ def released_on_rubygems?(name, version)
116
+ require "json"
117
+ require "net/http"
118
+ body = Net::HTTP.get(URI("https://rubygems.org/api/v1/gems/#{name}.json"))
119
+ JSON.parse(body)["version"] == version
120
+ rescue StandardError
121
+ false
122
+ end
108
123
  end
109
124
  end
@@ -162,8 +162,7 @@ module Gemchain
162
162
  name = step[:gem]
163
163
  version = step[:version] || bump_dependent(dir, name)
164
164
 
165
- files = [gemspec_file(dir), version_file(dir), File.join(dir, "Gemfile.lock")]
166
- .select { |f| File.exist?(f) }
165
+ files = release_files(dir)
167
166
  @runner.run(["git", "add", *files], chdir: dir)
168
167
 
169
168
  message = if step[:dependency]
@@ -179,6 +178,21 @@ module Gemchain
179
178
  @runner.run(["git", "push", "origin", "HEAD", "--tags"], chdir: dir)
180
179
  end
181
180
 
181
+ # Gemspec and version file are always staged; Gemfile.lock only when it
182
+ # exists and isn't deliberately excluded via .gitignore (gem repos
183
+ # commonly ignore it).
184
+ def release_files(dir)
185
+ files = [gemspec_file(dir), version_file(dir)]
186
+ lockfile = File.join(dir, "Gemfile.lock")
187
+ files << lockfile if File.exist?(lockfile) && !git_ignored?(dir, lockfile)
188
+ files
189
+ end
190
+
191
+ def git_ignored?(dir, file)
192
+ _out, _err, status = Open3.capture3("git", "check-ignore", "--no-index", "-q", file, chdir: dir)
193
+ status.success?
194
+ end
195
+
182
196
  # Dependents get an automatic version bump (they're being re-released
183
197
  # with a tightened constraint); the source gem's version comes from the
184
198
  # :bump step.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gemchain
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
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.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto