rake-gem-maintenance 0.1.7 → 0.2.0

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: 5b787e033da5eee96ab47d3b7aef6db19b4ba37cd00c5a6399df65382b7931cb
4
- data.tar.gz: b621f3b580ade530a9da0848f65fa734f0bd5555cfb47ebed826ec91a9535727
3
+ metadata.gz: 1025716123e134ab6db2ffcfc5655a59208dd038691b73e270c92c6bd35ea977
4
+ data.tar.gz: 1445566f68985cdd2b915c9958ec9daddaf07caa5002c62277481b72a122d561
5
5
  SHA512:
6
- metadata.gz: ccdac7428f63ab9a174667221f0071099784643d2a0087190fac484265cf8e54424c9f6b999778b5d00a5e1443f7f3eea307be8aa90599989d72d51aedac2487
7
- data.tar.gz: 64c1c90ac23717da73b24edcb6b6753fd6f2fc5c6b1b0b1f090c43d5502c5f21ab549fee8bc3847ab705f335fc787d49732b9b5ccf2c9b198672576746fdde90
6
+ metadata.gz: ba35f363319b323fe927da2cc3a9e8934d7b4310f5995b31d1cc91a759b34c8462262c449ef59fa1debe3c35615c733ef8f0dc96e6f270b592a76f1f6cc76153
7
+ data.tar.gz: 892b319be15819eb07cf5f254e9e7144e8387393a586442e9831585602600ade156e6d6c7c0b1dc41b6e60ee9e58b411c10a1bcebbff5e08f8510ec56f32ffad
data/CLAUDE.md CHANGED
@@ -22,7 +22,7 @@ rake rubocop
22
22
 
23
23
  ## Architecture
24
24
 
25
- ### Core Classes (lib/rake/gem_maintenance/)
25
+ ### Core Classes (lib/rake/gem/maintenance/)
26
26
 
27
27
  - **UpgradeTask** — Rake::TaskLib subclass defining upgrade:* tasks (branch, gems, commit, push, auto)
28
28
  - Default repositories: rubygems.org
@@ -36,21 +36,21 @@ rake rubocop
36
36
 
37
37
  ### Entry Points
38
38
 
39
- - `rake/gem_maintenance.rb` — requires all task classes
40
- - `rake/gem_maintenance/install_tasks.rb` — auto-instantiates UpgradeTask and VersionBumpTask 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
41
 
42
42
  ### Quick Usage
43
43
 
44
44
  ```ruby
45
45
  # Internal gems only (cbp-org.internal)
46
- Rake::GemMaintenance::InternalUpgradeTask.new
46
+ Rake::Gem::Maintenance::InternalUpgradeTask.new
47
47
 
48
48
  # Both repositories
49
- Rake::GemMaintenance::DualUpgradeTask.new
49
+ Rake::Gem::Maintenance::DualUpgradeTask.new
50
50
 
51
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
52
+ Rake::Gem::Maintenance::UpgradeTask.new do |t|
53
+ t.gem_repositories = Rake::Gem::Maintenance::Repos.internal # or Repos.all
54
54
  end
55
55
  ```
56
56
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rake-gem-maintenance (0.1.7)
4
+ rake-gem-maintenance (0.2.0)
5
5
  bundler-audit
6
6
  gem-release
7
7
  rake
@@ -254,7 +254,7 @@ CHECKSUMS
254
254
  racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
255
255
  rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
256
256
  rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
257
- rake-gem-maintenance (0.1.7)
257
+ rake-gem-maintenance (0.2.0)
258
258
  rb-fsevent (0.11.2) sha256=43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe
259
259
  rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e
260
260
  rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Rake::GemMaintenance
1
+ # Rake::Gem::Maintenance
2
2
 
3
3
  [![Ruby](https://github.com/cbroult/rake-gem-maintenance/actions/workflows/main.yml/badge.svg)](https://github.com/cbroult/rake-gem-maintenance/actions/workflows/main.yml)
4
4
  [![Dependabot](https://img.shields.io/badge/dependabot-enabled-blue?logo=dependabot)](https://github.com/cbroult/rake-gem-maintenance/network/updates)
@@ -18,7 +18,7 @@ gem "rake-gem-maintenance"
18
18
  Add to your Rakefile for default behavior:
19
19
 
20
20
  ```ruby
21
- require "rake/gem_maintenance/install_tasks"
21
+ require "rake/gem/maintenance/install_tasks"
22
22
  ```
23
23
 
24
24
  This defines:
@@ -33,15 +33,15 @@ This defines:
33
33
  ## Customization
34
34
 
35
35
  ```ruby
36
- require "rake/gem_maintenance"
36
+ require "rake/gem/maintenance"
37
37
 
38
- Rake::GemMaintenance::UpgradeTask.new do |t|
38
+ Rake::Gem::Maintenance::UpgradeTask.new do |t|
39
39
  t.main_branch = "develop"
40
40
  t.upgrade_branch = "chore/upgrade-deps"
41
41
  t.commit_message = "chore: upgrade dependencies"
42
42
  end
43
43
 
44
- Rake::GemMaintenance::VersionBumpTask.new do |t|
44
+ Rake::Gem::Maintenance::VersionBumpTask.new do |t|
45
45
  t.default_type = "minor"
46
46
  end
47
47
  ```
@@ -76,7 +76,7 @@ The file is `0600` (owner-read-only on Unix). The **password is never written to
76
76
 
77
77
  ### Step 2 — All future local runs are automatic
78
78
 
79
- Any project using `require "rake/gem_maintenance/install_tasks"` automatically reads the
79
+ Any project using `require "rake/gem/maintenance/install_tasks"` automatically reads the
80
80
  credential file at startup and sets `GEM_HOST_API_KEY` and `RUBYGEMS_OTP_SEED` in the process
81
81
  environment. Running `rake upgrade` needs no manual credential setup from this point on.
82
82
 
@@ -106,10 +106,10 @@ See [features/upgrade_task/renew_api_key.feature](features/upgrade_task/renew_ap
106
106
  ```ruby
107
107
  require "rake/gem/maintenance"
108
108
 
109
- Rake::GemMaintenance::Repos.rubygems_api_key_env_var = "MY_RUBYGEMS_KEY"
110
- Rake::GemMaintenance::Repos.rubygems_otp_seed_env_var = "MY_OTP_SEED"
109
+ Rake::Gem::Maintenance::Repos.rubygems_api_key_env_var = "MY_RUBYGEMS_KEY"
110
+ Rake::Gem::Maintenance::Repos.rubygems_otp_seed_env_var = "MY_OTP_SEED"
111
111
 
112
- Rake::GemMaintenance::UpgradeTask.new
112
+ Rake::Gem::Maintenance::UpgradeTask.new
113
113
  ```
114
114
 
115
115
  See [features/upgrade_task/repos_configuration.feature](features/upgrade_task/repos_configuration.feature)
data/TODO.md CHANGED
@@ -1 +1,4 @@
1
- * Relocate to Rake::Gem::Maintenance so that `rake bump` works (it fails currently).
1
+ # Packwerk
2
+
3
+ Packwerk is Shopify's package enforcement tool designed for large Rails monoliths.
4
+ With ~15 files in this library gem, the overhead is not warranted — close without implementing.
@@ -1,47 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rake
4
- module GemMaintenance
5
- # Renews a rubygems.org API key using credentials from env vars and
6
- # persists the new key to Woodpecker CI when server details are available.
7
- class ApiKeyRenewer
8
- def initialize(otp_provider:,
9
- username_env_var: "RUBYGEMS_USERNAME",
10
- password_env_var: "RUBYGEMS_PASSWORD")
11
- @otp_provider = otp_provider
12
- @username_env_var = username_env_var
13
- @password_env_var = password_env_var
14
- end
4
+ module Gem
5
+ module Maintenance
6
+ # Renews a rubygems.org API key using credentials from env vars and
7
+ # persists the new key to Woodpecker CI when server details are available.
8
+ class ApiKeyRenewer
9
+ def initialize(otp_provider:,
10
+ username_env_var: "RUBYGEMS_USERNAME",
11
+ password_env_var: "RUBYGEMS_PASSWORD")
12
+ @otp_provider = otp_provider
13
+ @username_env_var = username_env_var
14
+ @password_env_var = password_env_var
15
+ end
15
16
 
16
- def renew(repository)
17
- username = env_credential(@username_env_var)
18
- password = env_credential(@password_env_var)
19
- return nil if username.nil? || password.nil?
17
+ def renew(repository)
18
+ username = env_credential(@username_env_var)
19
+ password = env_credential(@password_env_var)
20
+ return nil if username.nil? || password.nil?
20
21
 
21
- otp = @otp_provider.otp_for(repository[:name], otp_seed_env_var: repository[:otp_seed_env_var])
22
- new_key = RubyGemsApiKeyCreator.new(host: repository.fetch(:url, "https://rubygems.org"))
23
- .create(username, password, otp: otp)
24
- persist_to_woodpecker(new_key)
25
- new_key
26
- rescue StandardError
27
- nil
28
- end
22
+ otp = @otp_provider.otp_for(repository[:name], otp_seed_env_var: repository[:otp_seed_env_var])
23
+ new_key = RubyGemsApiKeyCreator.new(host: repository.fetch(:url, "https://rubygems.org"))
24
+ .create(username, password, otp: otp)
25
+ persist_to_woodpecker(new_key)
26
+ new_key
27
+ rescue StandardError
28
+ nil
29
+ end
29
30
 
30
- private
31
+ private
31
32
 
32
- def env_credential(var)
33
- value = ENV.fetch(var, nil)
34
- value&.empty? ? nil : value
35
- end
33
+ def env_credential(var)
34
+ value = ENV.fetch(var, nil)
35
+ value&.empty? ? nil : value
36
+ end
36
37
 
37
- def persist_to_woodpecker(new_key)
38
- server = ENV.fetch("WOODPECKER_SERVER", nil)
39
- token = ENV.fetch("WOODPECKER_TOKEN", nil)
40
- return unless server && token
38
+ def persist_to_woodpecker(new_key)
39
+ server = ENV.fetch("WOODPECKER_SERVER", nil)
40
+ token = ENV.fetch("WOODPECKER_TOKEN", nil)
41
+ return unless server && token
41
42
 
42
- org = ENV.fetch("WOODPECKER_ORG", "cbp-org")
43
- WoodpeckerSecretStore.new(server: server, org: org, token: token)
44
- .store("rubygems_api_key", new_key)
43
+ org = ENV.fetch("WOODPECKER_ORG", "cbp-org")
44
+ WoodpeckerSecretStore.new(server: server, org: org, token: token)
45
+ .store("rubygems_api_key", new_key)
46
+ end
45
47
  end
46
48
  end
47
49
  end
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rake
4
- module GemMaintenance
5
- # Detects whether the current process is running inside a CI environment.
6
- module CIEnvironment
7
- def self.ci?
8
- ENV["CI"].to_s != ""
4
+ module Gem
5
+ module Maintenance
6
+ # Detects whether the current process is running inside a CI environment.
7
+ module CIEnvironment
8
+ def self.ci?
9
+ ENV["CI"].to_s != ""
10
+ end
9
11
  end
10
12
  end
11
13
  end
@@ -5,60 +5,62 @@ require "fileutils"
5
5
  require "rubygems"
6
6
 
7
7
  module Rake
8
- module GemMaintenance
9
- # Persists rubygems.org credentials (username, API key, OTP seed) to a local config file.
10
- class CredentialStore
11
- def self.default_path
12
- base = if Gem.win_platform?
13
- ENV.fetch("APPDATA", File.expand_path("~"))
14
- else
15
- ENV.fetch("XDG_CONFIG_HOME", File.join(Dir.home, ".config"))
16
- end
17
- File.join(base, "rake-gem-maintenance", "credentials.yml")
18
- end
8
+ module Gem
9
+ module Maintenance
10
+ # Persists rubygems.org credentials (username, API key, OTP seed) to a local config file.
11
+ class CredentialStore
12
+ def self.default_path
13
+ base = if ::Gem.win_platform?
14
+ ENV.fetch("APPDATA", File.expand_path("~"))
15
+ else
16
+ ENV.fetch("XDG_CONFIG_HOME", File.join(Dir.home, ".config"))
17
+ end
18
+ File.join(base, "rake-gem-maintenance", "credentials.yml")
19
+ end
19
20
 
20
- def initialize(path: self.class.default_path)
21
- @path = path
22
- end
21
+ def initialize(path: self.class.default_path)
22
+ @path = path
23
+ end
23
24
 
24
- attr_reader :path
25
+ attr_reader :path
25
26
 
26
- def read
27
- return {} unless File.exist?(@path)
27
+ def read
28
+ return {} unless File.exist?(@path)
28
29
 
29
- YAML.safe_load_file(@path, symbolize_names: true) || {}
30
- rescue StandardError
31
- {}
32
- end
30
+ YAML.safe_load_file(@path, symbolize_names: true) || {}
31
+ rescue StandardError
32
+ {}
33
+ end
33
34
 
34
- def write(credentials)
35
- FileUtils.mkdir_p(File.dirname(@path))
36
- File.write(@path, credentials.transform_keys(&:to_s).to_yaml)
37
- File.chmod(0o600, @path) unless Gem.win_platform?
38
- end
35
+ def write(credentials)
36
+ FileUtils.mkdir_p(File.dirname(@path))
37
+ File.write(@path, credentials.transform_keys(&:to_s).to_yaml)
38
+ File.chmod(0o600, @path) unless ::Gem.win_platform?
39
+ end
39
40
 
40
- def apply_to_env(username_env_var:, api_key_env_var:)
41
- creds = read
42
- set_env_if_absent(username_env_var, creds[:username])
43
- set_env_if_absent("RUBYGEMS_OTP_SEED", creds[:rubygems_otp_seed])
44
- set_env_if_absent(api_key_env_var, creds[:gem_host_api_key])
45
- end
41
+ def apply_to_env(username_env_var:, api_key_env_var:)
42
+ creds = read
43
+ set_env_if_absent(username_env_var, creds[:username])
44
+ set_env_if_absent("RUBYGEMS_OTP_SEED", creds[:rubygems_otp_seed])
45
+ set_env_if_absent(api_key_env_var, creds[:gem_host_api_key])
46
+ end
46
47
 
47
- def update(username:, api_key:, api_key_env_var:)
48
- otp_seed = ENV.fetch("RUBYGEMS_OTP_SEED", nil)
49
- updated = read.merge(username: username, gem_host_api_key: api_key)
50
- updated[:rubygems_otp_seed] = otp_seed if otp_seed && !otp_seed.empty?
51
- write(updated)
52
- ENV[api_key_env_var] = api_key
53
- end
48
+ def update(username:, api_key:, api_key_env_var:)
49
+ otp_seed = ENV.fetch("RUBYGEMS_OTP_SEED", nil)
50
+ updated = read.merge(username: username, gem_host_api_key: api_key)
51
+ updated[:rubygems_otp_seed] = otp_seed if otp_seed && !otp_seed.empty?
52
+ write(updated)
53
+ ENV[api_key_env_var] = api_key
54
+ end
54
55
 
55
- private
56
+ private
56
57
 
57
- def set_env_if_absent(env_var, value)
58
- return unless value && !value.empty?
59
- return if (existing = ENV.fetch(env_var, nil)) && !existing.empty?
58
+ def set_env_if_absent(env_var, value)
59
+ return unless value && !value.empty?
60
+ return if (existing = ENV.fetch(env_var, nil)) && !existing.empty?
60
61
 
61
- ENV[env_var] = value
62
+ ENV[env_var] = value
63
+ end
62
64
  end
63
65
  end
64
66
  end
@@ -4,113 +4,115 @@ require "json"
4
4
  require "open-uri"
5
5
 
6
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
- otp_provider: OtpProvider.new,
14
- ci_environment: CIEnvironment)
15
- @repositories = repositories
16
- @otp_provider = otp_provider
17
- @ci_environment = ci_environment
18
- @warnings = []
19
- @failed_pushes = []
20
- @failed_repositories = []
21
- @published_files = []
22
- @successful_repos = []
23
- end
7
+ module Gem
8
+ module Maintenance
9
+ # Publishes gems to multiple gem repositories, with version checking and warning handling.
10
+ class GemPublisher
11
+ attr_reader :repositories, :warnings, :failed_repositories, :successful_repos
12
+
13
+ def initialize(repositories = default_repositories,
14
+ otp_provider: OtpProvider.new,
15
+ ci_environment: CIEnvironment)
16
+ @repositories = repositories
17
+ @otp_provider = otp_provider
18
+ @ci_environment = ci_environment
19
+ @warnings = []
20
+ @failed_pushes = []
21
+ @failed_repositories = []
22
+ @published_files = []
23
+ @successful_repos = []
24
+ end
24
25
 
25
- def default_repositories
26
- [{ name: "rubygems", url: "https://rubygems.org" }]
27
- end
26
+ def default_repositories
27
+ [{ name: "rubygems", url: "https://rubygems.org" }]
28
+ end
28
29
 
29
- def publish(gem_file)
30
- @failed_pushes = []
31
- @published_files = []
32
- build_and_push(gem_file)
33
- print_warnings
34
- end
30
+ def publish(gem_file)
31
+ @failed_pushes = []
32
+ @published_files = []
33
+ build_and_push(gem_file)
34
+ print_warnings
35
+ end
35
36
 
36
- def check_all_repositories(gem_name)
37
- @failed_repositories = []
38
- repositories.each do |repo|
39
- versions_on_repository(gem_name, repo)
37
+ def check_all_repositories(gem_name)
38
+ @failed_repositories = []
39
+ repositories.each do |repo|
40
+ versions_on_repository(gem_name, repo)
41
+ end
40
42
  end
41
- end
42
43
 
43
- def versions_on_repository(gem_name, repository)
44
- url = "#{repository[:url]}/api/v1/gems/#{gem_name}.json"
45
- uri = URI.parse(url)
46
- response = uri.read(accept: "application/json")
47
- data = JSON.parse(response)
48
- data["versions"].map { |v| v["number"] }
49
- rescue StandardError => e
50
- @failed_repositories << repository[:name]
51
- @warnings << { repository: repository[:name], error: "Cannot fetch versions: #{e.message}" }
52
- []
53
- end
44
+ def versions_on_repository(gem_name, repository)
45
+ url = "#{repository[:url]}/api/v1/gems/#{gem_name}.json"
46
+ uri = URI.parse(url)
47
+ response = uri.read(accept: "application/json")
48
+ data = JSON.parse(response)
49
+ data["versions"].map { |v| v["number"] }
50
+ rescue StandardError => e
51
+ @failed_repositories << repository[:name]
52
+ @warnings << { repository: repository[:name], error: "Cannot fetch versions: #{e.message}" }
53
+ []
54
+ end
54
55
 
55
- def next_version(gem_name, current_version)
56
- return current_version unless current_version
56
+ def next_version(gem_name, current_version)
57
+ return current_version unless current_version
57
58
 
58
- ver = Gem::Version.new(current_version)
59
- loop do
60
- return ver.to_s unless version_exists_on_all_repos?(gem_name, ver)
59
+ ver = ::Gem::Version.new(current_version)
60
+ loop do
61
+ return ver.to_s unless version_exists_on_all_repos?(gem_name, ver)
61
62
 
62
- ver = ver.bump
63
+ ver = ver.bump
64
+ end
63
65
  end
64
- end
65
66
 
66
- def available_repositories
67
- repositories.map { |r| r[:name] } - @failed_repositories
68
- end
67
+ def available_repositories
68
+ repositories.map { |r| r[:name] } - @failed_repositories
69
+ end
69
70
 
70
- def any_available?
71
- @failed_repositories.size < repositories.size
72
- end
71
+ def any_available?
72
+ @failed_repositories.size < repositories.size
73
+ end
73
74
 
74
- private
75
+ private
75
76
 
76
- def build_and_push(gem_file)
77
- repositories.each do |repo|
78
- push(gem_file, repository: repo)
77
+ def build_and_push(gem_file)
78
+ repositories.each do |repo|
79
+ push(gem_file, repository: repo)
80
+ end
79
81
  end
80
- end
81
82
 
82
- def push(gem_file, repository:)
83
- result = GemPush.new(gem_file, repository, @otp_provider).attempt
84
- return record_success(gem_file, repository) if result.success
83
+ def push(gem_file, repository:)
84
+ result = GemPush.new(gem_file, repository, @otp_provider).attempt
85
+ return record_success(gem_file, repository) if result.success
85
86
 
86
- record_push_failure(repository, result.error)
87
- rescue StandardError => e
88
- @failed_pushes << { repository: repository[:name], error: e.message }
89
- end
87
+ record_push_failure(repository, result.error)
88
+ rescue StandardError => e
89
+ @failed_pushes << { repository: repository[:name], error: e.message }
90
+ end
90
91
 
91
- def record_success(gem_file, repository)
92
- @published_files << gem_file
93
- @successful_repos << repository[:name]
94
- end
92
+ def record_success(gem_file, repository)
93
+ @published_files << gem_file
94
+ @successful_repos << repository[:name]
95
+ end
95
96
 
96
- def record_push_failure(repository, message)
97
- @failed_pushes << { repository: repository[:name], error: message.strip }
98
- end
97
+ def record_push_failure(repository, message)
98
+ @failed_pushes << { repository: repository[:name], error: message.strip }
99
+ end
99
100
 
100
- def version_exists_on_all_repos?(gem_name, version)
101
- repositories.all? do |repo|
102
- versions_on_repository(gem_name, repo).include?(version.to_s)
101
+ def version_exists_on_all_repos?(gem_name, version)
102
+ repositories.all? do |repo|
103
+ versions_on_repository(gem_name, repo).include?(version.to_s)
104
+ end
103
105
  end
104
- end
105
106
 
106
- def print_warnings
107
- return if @failed_pushes.empty?
107
+ def print_warnings
108
+ return if @failed_pushes.empty?
108
109
 
109
- puts "[WARN] The following repositories were unavailable:"
110
- @failed_pushes.each do |failure|
111
- puts " - #{failure[:repository]}: #{failure[:error]}"
110
+ puts "[WARN] The following repositories were unavailable:"
111
+ @failed_pushes.each do |failure|
112
+ puts " - #{failure[:repository]}: #{failure[:error]}"
113
+ end
114
+ puts "[WARN] Warning: Published to #{@published_files.size} of #{repositories.size} repositories"
112
115
  end
113
- puts "[WARN] Warning: Published to #{@published_files.size} of #{repositories.size} repositories"
114
116
  end
115
117
  end
116
118
  end
@@ -3,58 +3,60 @@
3
3
  require "open3"
4
4
 
5
5
  module Rake
6
- module GemMaintenance
7
- # Pushes a single gem file to one repository, retrying with a renewed API key on auth failure.
8
- class GemPush
9
- Result = Struct.new(:success, :error)
10
-
11
- def initialize(gem_file, repository, otp_provider)
12
- @gem_file = gem_file
13
- @repository = repository
14
- @otp_provider = otp_provider
15
- end
6
+ module Gem
7
+ module Maintenance
8
+ # Pushes a single gem file to one repository, retrying with a renewed API key on auth failure.
9
+ class GemPush
10
+ Result = Struct.new(:success, :error)
16
11
 
17
- def attempt
18
- out_err, status = Open3.capture2e(env, command)
19
- return Result.new(true, nil) if status.success?
20
- return retry_with_renewed_key if auth_failure?(out_err)
12
+ def initialize(gem_file, repository, otp_provider)
13
+ @gem_file = gem_file
14
+ @repository = repository
15
+ @otp_provider = otp_provider
16
+ end
21
17
 
22
- Result.new(false, out_err)
23
- end
18
+ def attempt
19
+ out_err, status = Open3.capture2e(env, command)
20
+ return Result.new(true, nil) if status.success?
21
+ return retry_with_renewed_key if auth_failure?(out_err)
24
22
 
25
- private
23
+ Result.new(false, out_err)
24
+ end
26
25
 
27
- def command
28
- cmd = "gem push #{@gem_file} --host #{@repository[:url]}"
29
- otp = @otp_provider.otp_for(@repository[:name], otp_seed_env_var: @repository[:otp_seed_env_var])
30
- cmd += " --otp #{otp}" if otp
31
- cmd
32
- end
26
+ private
33
27
 
34
- def env
35
- env_var = @repository[:api_key_env_var]
36
- return {} unless env_var
28
+ def command
29
+ cmd = "gem push #{@gem_file} --host #{@repository[:url]}"
30
+ otp = @otp_provider.otp_for(@repository[:name], otp_seed_env_var: @repository[:otp_seed_env_var])
31
+ cmd += " --otp #{otp}" if otp
32
+ cmd
33
+ end
37
34
 
38
- key = ENV.fetch(env_var, nil)
39
- return {} if key.nil? || key.empty?
35
+ def env
36
+ env_var = @repository[:api_key_env_var]
37
+ return {} unless env_var
40
38
 
41
- { "GEM_HOST_API_KEY" => key }
42
- end
39
+ key = ENV.fetch(env_var, nil)
40
+ return {} if key.nil? || key.empty?
43
41
 
44
- def auth_failure?(output)
45
- output.match?(/unauthorized|api.key|forbidden/i) ||
46
- output.include?("401") || output.include?("403")
47
- end
42
+ { "GEM_HOST_API_KEY" => key }
43
+ end
44
+
45
+ def auth_failure?(output)
46
+ output.match?(/unauthorized|api.key|forbidden/i) ||
47
+ output.include?("401") || output.include?("403")
48
+ end
48
49
 
49
- def retry_with_renewed_key
50
- new_key = ApiKeyRenewer.new(otp_provider: @otp_provider).renew(@repository)
51
- return Result.new(false, "Auth failed and renewal credentials unavailable.") unless new_key
50
+ def retry_with_renewed_key
51
+ new_key = ApiKeyRenewer.new(otp_provider: @otp_provider).renew(@repository)
52
+ return Result.new(false, "Auth failed and renewal credentials unavailable.") unless new_key
52
53
 
53
- _out_err, status = Open3.capture2e(env.merge("GEM_HOST_API_KEY" => new_key), command)
54
- if status.success?
55
- Result.new(true, nil)
56
- else
57
- Result.new(false, "Push failed after key renewal.")
54
+ _out_err, status = Open3.capture2e(env.merge("GEM_HOST_API_KEY" => new_key), command)
55
+ if status.success?
56
+ Result.new(true, nil)
57
+ else
58
+ Result.new(false, "Push failed after key renewal.")
59
+ end
58
60
  end
59
61
  end
60
62
  end