rake-gem-maintenance 0.1.3 → 0.1.4

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: 302cc96536adfb7335226c0dc22db143e71a54946e601f1728f54acf6c2ba63f
4
- data.tar.gz: 5d10d6e3c5ff947b3ba6c54428dd7dd27f6e118384711b483a2dd78d2bd4cae0
3
+ metadata.gz: 56b2317bfd8c0682d6ec738cec517684acbc61618a5cdabbae392f6b3af1585a
4
+ data.tar.gz: 0bc94e5f08ec7f816a23ad7d01afc947df12b14518f5a59c50984725aa3a4e67
5
5
  SHA512:
6
- metadata.gz: 8dcb8e1ee9051c3d3747f265e0acf299dd0121d678fb0805813e6fc41d689b32c443b7182061157ac20693eae772d18f6def2ad72e55d2cb1bee5e80a31f9836
7
- data.tar.gz: 6be86b064ce5f8a0e19c9f6bd495a8192d6d4728395e0a3b5bc694cc6bb77c5eb906422384e507b05820d4deacea6527d76edf3ddc6f7a0c89e4c6862cc149a9
6
+ metadata.gz: b7a7178dcae40451f051de63bb8cd64ad9559d5fa91a47cb1cf65cbd188db088e7cd1c88b6e06ac6b95947b5460d2d8a3f38ff6f778a9d327a418171cf5782e4
7
+ data.tar.gz: f714225b46c59edc8882db10fe26afded7eae11f89c06573b4a0fe5ac8effa9b312f945efe3df706989d3c9a1d3ee6513347b38b177052138b418c9ea355596f
data/CLAUDE.md CHANGED
@@ -25,12 +25,34 @@ rake rubocop
25
25
  ### Core Classes (lib/rake/gem_maintenance/)
26
26
 
27
27
  - **UpgradeTask** — Rake::TaskLib subclass defining upgrade:* tasks (branch, gems, commit, push, auto)
28
+ - Default repositories: rubygems.org
29
+ - **InternalUpgradeTask** — Rake::TaskLib subclass for internal gems only
30
+ - Default repositories: gems.cbp-org.internal
31
+ - **DualUpgradeTask** — Rake::TaskLib subclass for gems published to both rubygems.org and cbp-org
32
+ - Default repositories: both rubygems.org and cbp-org.internal
28
33
  - **VersionBumpTask** — Rake::TaskLib subclass defining version:bump[type] and bump[type] tasks
34
+ - **GemPublisher** — Handles publishing gems to multiple repositories, collecting warnings for failed pushes
35
+ - **Repos** — Pre-configured repository sets (internal, all, rubygems, default)
29
36
 
30
37
  ### Entry Points
31
38
 
32
- - `rake/gem_maintenance.rb` — requires both task classes
33
- - `rake/gem_maintenance/install_tasks.rb` — auto-instantiates both with defaults
39
+ - `rake/gem_maintenance.rb` — requires all task classes
40
+ - `rake/gem_maintenance/install_tasks.rb` — auto-instantiates UpgradeTask and VersionBumpTask with defaults
41
+
42
+ ### Quick Usage
43
+
44
+ ```ruby
45
+ # Internal gems only (cbp-org.internal)
46
+ Rake::GemMaintenance::InternalUpgradeTask.new
47
+
48
+ # Both repositories
49
+ Rake::GemMaintenance::DualUpgradeTask.new
50
+
51
+ # Default (rubygems.org) - can also use Repos module
52
+ Rake::GemMaintenance::UpgradeTask.new do |t|
53
+ t.gem_repositories = Rake::GemMaintenance::Repos.internal # or Repos.all
54
+ end
55
+ ```
34
56
 
35
57
  ## Commit Conventions
36
58
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rake-gem-maintenance (0.1.3)
4
+ rake-gem-maintenance (0.1.4)
5
5
  bundler-audit
6
6
  gem-release (~> 2.2)
7
7
  rake (>= 13.0)
@@ -99,7 +99,7 @@ GEM
99
99
  nenv (~> 0.1)
100
100
  shellany (~> 0.0)
101
101
  ostruct (0.6.3)
102
- parallel (2.0.1)
102
+ parallel (2.1.0)
103
103
  parser (3.3.11.1)
104
104
  ast (~> 2.4.1)
105
105
  racc
@@ -226,14 +226,14 @@ CHECKSUMS
226
226
  nenv (0.3.0) sha256=d9de6d8fb7072228463bf61843159419c969edb34b3cef51832b516ae7972765
227
227
  notiffany (0.1.3) sha256=d37669605b7f8dcb04e004e6373e2a780b98c776f8eb503ac9578557d7808738
228
228
  ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
229
- parallel (2.0.1) sha256=337782d3e39f4121e67563bf91dd8ece67f48923d90698614773a0ec9a5b2c7d
229
+ parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
230
230
  parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54
231
231
  prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
232
232
  pry (0.16.0) sha256=d76c69065698ed1f85e717bd33d7942c38a50868f6b0673c636192b3d1b6054e
233
233
  racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
234
234
  rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
235
235
  rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
236
- rake-gem-maintenance (0.1.3)
236
+ rake-gem-maintenance (0.1.4)
237
237
  rb-fsevent (0.11.2) sha256=43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe
238
238
  rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e
239
239
  regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "open-uri"
5
+
6
+ module Rake
7
+ module GemMaintenance
8
+ # Publishes gems to multiple gem repositories, with version checking and warning handling.
9
+ class GemPublisher
10
+ attr_reader :repositories, :warnings, :failed_repositories, :successful_repos
11
+
12
+ def initialize(repositories = default_repositories)
13
+ @repositories = repositories
14
+ @warnings = []
15
+ @failed_pushes = []
16
+ @failed_repositories = []
17
+ @published_files = []
18
+ @successful_repos = []
19
+ end
20
+
21
+ def default_repositories
22
+ [{ name: "rubygems", url: "https://rubygems.org" }]
23
+ end
24
+
25
+ def publish(gem_file)
26
+ @failed_pushes = []
27
+ @published_files = []
28
+ build_and_push(gem_file)
29
+ print_warnings
30
+ end
31
+
32
+ def check_all_repositories(gem_name)
33
+ @failed_repositories = []
34
+ repositories.each do |repo|
35
+ versions_on_repository(gem_name, repo)
36
+ end
37
+ end
38
+
39
+ def versions_on_repository(gem_name, repository)
40
+ url = "#{repository[:url]}/api/v1/gems/#{gem_name}.json"
41
+ uri = URI.parse(url)
42
+ response = uri.read(accept: "application/json")
43
+ data = JSON.parse(response)
44
+ data["versions"].map { |v| v["number"] }
45
+ rescue StandardError => e
46
+ @failed_repositories << repository[:name]
47
+ @warnings << { repository: repository[:name], error: "Cannot fetch versions: #{e.message}" }
48
+ []
49
+ end
50
+
51
+ def next_version(gem_name, current_version)
52
+ return current_version unless current_version
53
+
54
+ ver = Gem::Version.new(current_version)
55
+ loop do
56
+ return ver.to_s unless version_exists_on_all_repos?(gem_name, ver)
57
+
58
+ ver = ver.bump
59
+ end
60
+ end
61
+
62
+ def available_repositories
63
+ repositories.map { |r| r[:name] } - @failed_repositories
64
+ end
65
+
66
+ def any_available?
67
+ @failed_repositories.size < repositories.size
68
+ end
69
+
70
+ private
71
+
72
+ def build_and_push(gem_file)
73
+ repositories.each do |repo|
74
+ push(gem_file, repository: repo)
75
+ end
76
+ end
77
+
78
+ def push(gem_file, repository:)
79
+ cmd = "gem push #{gem_file} --host #{repository[:url]}"
80
+ result = system(cmd)
81
+ if result
82
+ @published_files << gem_file
83
+ @successful_repos << repository[:name]
84
+ end
85
+ rescue StandardError => e
86
+ @failed_pushes << { repository: repository[:name], error: e.message }
87
+ end
88
+
89
+ def version_exists_on_all_repos?(gem_name, version)
90
+ repositories.all? do |repo|
91
+ versions_on_repository(gem_name, repo).include?(version.to_s)
92
+ end
93
+ end
94
+
95
+ def print_warnings
96
+ return if @failed_pushes.empty?
97
+
98
+ puts "[WARN] The following repositories were unavailable:"
99
+ @failed_pushes.each do |failure|
100
+ puts " - #{failure[:repository]}: #{failure[:error]}"
101
+ end
102
+ puts "[WARN] Warning: Published to #{@published_files.size} of #{repositories.size} repositories"
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rake
4
+ module GemMaintenance
5
+ # Pre-configured gem repository configurations for common setups.
6
+ #
7
+ # @example Use internal-only repos
8
+ # Rake::GemMaintenance::UpgradeTask.new do |t|
9
+ # t.gem_repositories = Rake::GemMaintenance::Repos.internal
10
+ # end
11
+ #
12
+ # @example Use both rubygems.org and internal repos
13
+ # Rake::GemMaintenance::UpgradeTask.new do |t|
14
+ # t.gem_repositories = Rake::GemMaintenance::Repos.all
15
+ # end
16
+ #
17
+ # @example Reconfigure internal URL
18
+ # Rake::GemMaintenance::Repos.internal_url = "https://my-internal-gem.example.com"
19
+ module Repos
20
+ @internal_url = "https://gems.cbp-org.internal"
21
+ @rubygems_url = "https://rubygems.org"
22
+
23
+ class << self
24
+ attr_accessor :internal_url, :rubygems_url
25
+ end
26
+
27
+ # Publish only to internal repository
28
+ # @return [Array<Hash>] repository configuration
29
+ def self.internal
30
+ [{ name: "cbp-org", url: internal_url }]
31
+ end
32
+
33
+ # Publish to both rubygems.org and internal repository
34
+ # @return [Array<Hash>] repository configuration
35
+ def self.all
36
+ [
37
+ { name: "rubygems", url: rubygems_url },
38
+ { name: "cbp-org", url: internal_url }
39
+ ]
40
+ end
41
+
42
+ # Publish only to rubygems.org (the default)
43
+ # @return [Array<Hash>] repository configuration
44
+ def self.rubygems
45
+ [{ name: "rubygems", url: rubygems_url }]
46
+ end
47
+
48
+ # Default configuration: rubygems.org only
49
+ # @return [Array<Hash>] repository configuration
50
+ def self.default
51
+ rubygems
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,22 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "net/http"
3
4
  require "rake"
4
5
  require "rake/tasklib"
6
+ require_relative "gem_publisher"
7
+ require_relative "repos"
5
8
 
6
9
  module Rake
7
10
  module GemMaintenance
8
- # Defines Rake tasks for upgrading gem dependencies.
11
+ # Defines Rake tasks for upgrading gem dependencies and publishing to multiple repositories.
9
12
  #
10
- # Creates: upgrade, upgrade:auto, upgrade:branch, upgrade:gems,
11
- # upgrade:commit, upgrade:push
13
+ # Creates: upgrade, upgrade:auto, upgrade:branch, upgrade:gems, upgrade:commit,
14
+ # upgrade:prepare_version, upgrade:push
15
+ # rubocop:disable Metrics/ClassLength, Metrics/MethodLength
12
16
  class UpgradeTask < ::Rake::TaskLib
13
17
  attr_accessor :name, :main_branch, :upgrade_branch, :commit_message,
14
18
  :files_to_commit, :verification_task, :release_task,
15
19
  :version_bump_task, :update_rubygems, :update_gems,
16
- :run_bundle_audit, :auto_pipeline
20
+ :run_bundle_audit, :auto_pipeline, :gem_repositories,
21
+ :gem_publisher_class, :gem_name, :gem_version
17
22
 
18
- def initialize(name = :upgrade) # rubocop:disable Metrics/MethodLength
23
+ def initialize(name = :upgrade)
19
24
  super()
25
+ apply_default_configuration(name)
26
+
27
+ yield self if block_given?
28
+ define_tasks
29
+ end
30
+
31
+ def apply_default_configuration(name)
20
32
  @name = name
21
33
  @main_branch = "main"
22
34
  @upgrade_branch = "upgrade/gems"
@@ -29,15 +41,45 @@ module Rake
29
41
  @update_gems = true
30
42
  @run_bundle_audit = true
31
43
  @auto_pipeline = nil
32
-
33
- yield self if block_given?
34
- define_tasks
44
+ @gem_repositories = Repos.rubygems
45
+ @gem_publisher_class = GemPublisher
46
+ @gem_name = detect_gem_name
47
+ @gem_version = detect_gem_version
35
48
  end
36
49
 
37
50
  private
38
51
 
52
+ def detect_gem_name
53
+ gemspec = Dir.glob("*.gemspec").first
54
+ return nil unless gemspec
55
+
56
+ Gem::Specification.load(gemspec).name
57
+ end
58
+
59
+ def detect_gem_version
60
+ gemspec = Dir.glob("*.gemspec").first
61
+ return nil unless gemspec
62
+
63
+ Gem::Specification.load(gemspec).version.to_s
64
+ end
65
+
66
+ def repo_available?(repo)
67
+ url = repo[:url]
68
+ uri = URI.parse(url)
69
+ http = Net::HTTP.new(uri.host, uri.port)
70
+ http.use_ssl = uri.scheme == "https"
71
+ http.open_timeout = 5
72
+ http.start { |h| h.head("/").code.to_i < 400 }
73
+ rescue StandardError
74
+ false
75
+ end
76
+
77
+ public
78
+
39
79
  def define_tasks
40
80
  define_top_level_task
81
+ define_info_tasks
82
+ define_prepare_version_task
41
83
  define_auto_task
42
84
  define_branch_task
43
85
  define_gems_task
@@ -45,11 +87,65 @@ module Rake
45
87
  define_push_task
46
88
  end
47
89
 
90
+ def define_info_tasks
91
+ task_instance = self
92
+ namespace name do
93
+ namespace :info do
94
+ define_info_repos_task(task_instance)
95
+ define_info_version_task(task_instance)
96
+ define_info_name_task(task_instance)
97
+ desc "Show all upgrade info"
98
+ task all: %i[name version repos]
99
+ end
100
+ desc "Show all upgrade info"
101
+ task info: "#{name}:info:all"
102
+ end
103
+ end
104
+
105
+ def define_info_repos_task(task_instance)
106
+ desc "Show configured gem repositories"
107
+ task :repos do
108
+ puts "Gem repositories:"
109
+ task_instance.gem_repositories.each do |repo|
110
+ available = task_instance.send(:repo_available?, repo)
111
+ status = available ? "✓" : "✗"
112
+ avail_text = available ? "AVAILABLE" : "NOT AVAILABLE"
113
+ puts " - #{repo[:name]} (#{status}) - #{avail_text}: #{repo[:url]}"
114
+ end
115
+ end
116
+ end
117
+
118
+ def define_info_version_task(task_instance)
119
+ desc "Show current gem version"
120
+ task :version do
121
+ ver = task_instance.gem_version || "unknown"
122
+ puts "Current version: #{ver}"
123
+ end
124
+ end
125
+
126
+ def define_info_name_task(task_instance)
127
+ desc "Show current gem name"
128
+ task :name do
129
+ name = task_instance.gem_name || "unknown"
130
+ puts "Gem name: #{name}"
131
+ end
132
+ end
133
+
48
134
  def define_top_level_task
49
135
  desc "Alias for #{name}:auto"
50
136
  task name => "#{name}:auto"
51
137
  end
52
138
 
139
+ def define_prepare_version_task
140
+ task_instance = self
141
+ namespace name do
142
+ desc "Check version on all repositories before bumping"
143
+ task :prepare_version do
144
+ task_instance.send(:check_version_on_repositories)
145
+ end
146
+ end
147
+ end
148
+
53
149
  def define_auto_task
54
150
  task_instance = self
55
151
  namespace name do
@@ -93,7 +189,7 @@ module Rake
93
189
  def pipeline_tasks
94
190
  return auto_pipeline if auto_pipeline
95
191
 
96
- %i[branch gems] + [verification_task, :commit, version_bump_task.to_sym, release_task, :push]
192
+ %i[branch gems] + [verification_task, :commit, version_bump_task.to_sym, :prepare_version, release_task, :push]
97
193
  end
98
194
 
99
195
  def create_upgrade_branch
@@ -119,6 +215,84 @@ module Rake
119
215
  def push_branch
120
216
  sh "git push origin #{upgrade_branch}"
121
217
  end
218
+
219
+ # rubocop:disable Metrics/AbcSize
220
+ def check_version_on_repositories
221
+ unless gem_name && gem_version
222
+ puts "[ERROR] No gemspec found - cannot check version/upgrade"
223
+ abort
224
+ end
225
+
226
+ publisher = gem_publisher_class.new(gem_repositories)
227
+ gemspec_repos = gem_repositories.select { |repo| repo_available?(repo) }
228
+
229
+ if gemspec_repos.empty?
230
+ puts "[ERROR] No repositories available. Cannot check version."
231
+ abort
232
+ end
233
+
234
+ publisher.check_all_repositories(gem_name)
235
+
236
+ print_failed_repository_warnings(publisher)
237
+ version = gem_version
238
+ next_ver = publisher.next_version(gem_name, version)
239
+
240
+ if next_ver == version
241
+ puts "[INFO] Version #{version} not found on any repository - will publish"
242
+ else
243
+ puts "[INFO] Version #{version} already published to all repositories"
244
+ puts "[INFO] Next available version: #{next_ver}"
245
+ end
246
+
247
+ handle_partial_publish_warning(publisher, version)
248
+ end
249
+ # rubocop:enable Metrics/AbcSize
250
+
251
+ def handle_partial_publish_warning(publisher, version)
252
+ return if publisher.successful_repos.empty?
253
+
254
+ published = publisher.successful_repos
255
+ total = gem_repositories.size
256
+ return unless published.size < total
257
+
258
+ puts "[WARN] Version #{version} was only published to #{published.size} of #{total} repositories"
259
+ puts "[WARN] Run 'rake upgrade:prepare_version' manually to check status"
260
+ end
261
+
262
+ def repos_available?(publisher)
263
+ return true if publisher.any_available?
264
+
265
+ puts "[ERROR] No repositories available. Cannot check version."
266
+ abort
267
+ end
268
+
269
+ def print_failed_repository_warnings(publisher)
270
+ return if publisher.failed_repositories.empty?
271
+
272
+ puts "[WARN] The following repositories were unavailable:"
273
+ publisher.failed_repositories.each do |repo_name|
274
+ puts " - #{repo_name}"
275
+ end
276
+ end
277
+ # rubocop:enable Metrics/ClassLength, Metrics/MethodLength
278
+ end
279
+
280
+ # Upgrades gems and publishes to cbp-org only (internal gems).
281
+ # Uses Repos.internal as default repositories.
282
+ class InternalUpgradeTask < UpgradeTask
283
+ def apply_default_configuration(name)
284
+ super
285
+ @gem_repositories = Repos.internal
286
+ end
287
+ end
288
+
289
+ # Upgrades gems and publishes to both rubygems.org and cbp-org.
290
+ # Uses Repos.all as default repositories.
291
+ class DualUpgradeTask < UpgradeTask
292
+ def apply_default_configuration(name)
293
+ super
294
+ @gem_repositories = Repos.all
295
+ end
122
296
  end
123
297
  end
124
298
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rake
4
4
  module GemMaintenance
5
- VERSION = "0.1.3"
5
+ VERSION = "0.1.4"
6
6
  end
7
7
  end
@@ -2,4 +2,6 @@
2
2
 
3
3
  require_relative "maintenance/version"
4
4
  require_relative "maintenance/version_bump_task"
5
+ require_relative "maintenance/gem_publisher"
6
+ require_relative "maintenance/repos"
5
7
  require_relative "maintenance/upgrade_task"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rake-gem-maintenance
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christophe Broult
@@ -71,7 +71,9 @@ files:
71
71
  - TODO.md
72
72
  - cucumber.yml
73
73
  - lib/rake/gem/maintenance.rb
74
+ - lib/rake/gem/maintenance/gem_publisher.rb
74
75
  - lib/rake/gem/maintenance/install_tasks.rb
76
+ - lib/rake/gem/maintenance/repos.rb
75
77
  - lib/rake/gem/maintenance/upgrade_task.rb
76
78
  - lib/rake/gem/maintenance/version.rb
77
79
  - lib/rake/gem/maintenance/version_bump_task.rb