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 +4 -4
- data/CLAUDE.md +7 -7
- data/Gemfile.lock +2 -2
- data/README.md +9 -9
- data/TODO.md +4 -1
- data/lib/rake/gem/maintenance/api_key_renewer.rb +37 -35
- data/lib/rake/gem/maintenance/ci_environment.rb +7 -5
- data/lib/rake/gem/maintenance/credential_store.rb +46 -44
- data/lib/rake/gem/maintenance/gem_publisher.rb +86 -84
- data/lib/rake/gem/maintenance/gem_push.rb +44 -42
- data/lib/rake/gem/maintenance/install_tasks.rb +5 -5
- data/lib/rake/gem/maintenance/otp_provider.rb +33 -31
- data/lib/rake/gem/maintenance/renew_api_key_task.rb +111 -109
- data/lib/rake/gem/maintenance/repos.rb +81 -79
- data/lib/rake/gem/maintenance/ruby_gems_api_key_creator.rb +39 -37
- data/lib/rake/gem/maintenance/upgrade_task.rb +245 -242
- data/lib/rake/gem/maintenance/version.rb +4 -2
- data/lib/rake/gem/maintenance/version_bump_task.rb +83 -81
- data/lib/rake/gem/maintenance/woodpecker_secret_store.rb +69 -67
- data/scripts/ci_publish_rubygems.rb +3 -3
- metadata +1 -1
|
@@ -4,110 +4,112 @@ require "rake"
|
|
|
4
4
|
require "rake/tasklib"
|
|
5
5
|
|
|
6
6
|
module Rake
|
|
7
|
-
module
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
7
|
+
module Gem
|
|
8
|
+
module Maintenance
|
|
9
|
+
# Defines Rake tasks for version bumping with gem-release.
|
|
10
|
+
#
|
|
11
|
+
# Creates: version:bump[type] and bump[type] (alias)
|
|
12
|
+
class VersionBumpTask < ::Rake::TaskLib
|
|
13
|
+
VALID_TYPES = %w[patch minor major].freeze
|
|
14
|
+
|
|
15
|
+
attr_accessor :namespace_name, :default_type, :commit_message_template,
|
|
16
|
+
:create_alias, :amend_with_gemfile_lock
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
super
|
|
20
|
+
@namespace_name = :version
|
|
21
|
+
@default_type = "patch"
|
|
22
|
+
@commit_message_template = "chore(release): %<version>s"
|
|
23
|
+
@create_alias = true
|
|
24
|
+
@amend_with_gemfile_lock = true
|
|
25
|
+
|
|
26
|
+
yield self if block_given?
|
|
27
|
+
|
|
28
|
+
define_tasks
|
|
29
|
+
end
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
private
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
def define_tasks
|
|
34
|
+
define_version_bump_task
|
|
35
|
+
define_alias_task if create_alias
|
|
36
|
+
end
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
def define_version_bump_task
|
|
39
|
+
task_instance = self
|
|
40
|
+
bump_desc = "Bump version (patch, minor, major) and update Gemfile.lock. Default: #{default_type}"
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
namespace namespace_name do
|
|
43
|
+
desc bump_desc
|
|
44
|
+
task :bump, [:type] do |_t, args|
|
|
45
|
+
task_instance.send(:run_bump, args)
|
|
46
|
+
end
|
|
45
47
|
end
|
|
46
48
|
end
|
|
47
|
-
end
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
def define_alias_task
|
|
51
|
+
ns = namespace_name
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
desc "Alias for #{ns}:bump"
|
|
54
|
+
task :bump, [:type] => ["#{ns}:bump"]
|
|
55
|
+
end
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
def run_bump(args)
|
|
58
|
+
args.with_defaults(type: default_type)
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
validate_version_type(args.type)
|
|
61
|
+
execute_version_bump(args.type)
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
return unless amend_with_gemfile_lock
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
update_gemfile_lock
|
|
66
|
+
amend_commit_to_include_gemfile_lock_changes
|
|
67
|
+
end
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
def validate_version_type(type)
|
|
70
|
+
return if VALID_TYPES.include?(type)
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
abort "Error: Version type must be one of: #{VALID_TYPES.join(', ')}"
|
|
73
|
+
end
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
def execute_version_bump(type)
|
|
76
|
+
puts "Bumping #{type} version..."
|
|
77
|
+
version_file = detect_version_file
|
|
78
|
+
cmd = "bundle exec gem bump --version #{type}"
|
|
79
|
+
cmd += " --file #{version_file}" if version_file
|
|
80
|
+
bump_result = system("#{cmd} --message '#{commit_message_template}'")
|
|
81
|
+
return if bump_result
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
abort "Error: Failed to bump version"
|
|
84
|
+
end
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
def detect_version_file
|
|
87
|
+
gemspec_path = Dir.glob("*.gemspec").first
|
|
88
|
+
return nil unless gemspec_path
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
gem_name = ::Gem::Specification.load(gemspec_path).name
|
|
91
|
+
version_rb = File.join("lib", gem_name.tr("-", "/"), "version.rb")
|
|
92
|
+
return version_rb if File.exist?(version_rb)
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
version_rb = File.join("lib", gem_name.split("-").first, gem_name.split("-")[1..].join("_"), "version.rb")
|
|
95
|
+
return version_rb if File.exist?(version_rb)
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
def update_gemfile_lock
|
|
101
|
+
puts "Updating Gemfile.lock..."
|
|
102
|
+
bundle_result = system("bundle install")
|
|
103
|
+
return if bundle_result
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
abort "Error: Failed to update Gemfile.lock"
|
|
106
|
+
end
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
def amend_commit_to_include_gemfile_lock_changes
|
|
109
|
+
puts "Amending commit to include Gemfile.lock update..."
|
|
110
|
+
system("git add .")
|
|
111
|
+
system("git commit --amend --no-edit")
|
|
112
|
+
end
|
|
111
113
|
end
|
|
112
114
|
end
|
|
113
115
|
end
|
|
@@ -1,88 +1,90 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Rake
|
|
4
|
-
module
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
module Gem
|
|
5
|
+
module Maintenance
|
|
6
|
+
# Creates or updates an org-level secret in a Woodpecker CI server.
|
|
7
|
+
# SSL verification is disabled because Woodpecker is typically served
|
|
8
|
+
# on an internal network with a private CA.
|
|
9
|
+
class WoodpeckerSecretStore
|
|
10
|
+
def initialize(server:, org:, token:)
|
|
11
|
+
@server = server
|
|
12
|
+
@org = org
|
|
13
|
+
@token = token
|
|
14
|
+
end
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
def store(secret_name, value, events: %w[push tag manual])
|
|
17
|
+
org_id = find_org_id
|
|
18
|
+
abort "[ERROR] Woodpecker org '#{@org}' not found on #{@server}." unless org_id
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
if secret_exists?(org_id, secret_name)
|
|
21
|
+
patch("/api/orgs/#{org_id}/secrets/#{secret_name}",
|
|
22
|
+
{ value: value, events: events, images: [] })
|
|
23
|
+
else
|
|
24
|
+
post("/api/orgs/#{org_id}/secrets",
|
|
25
|
+
{ name: secret_name, value: value, events: events, images: [] })
|
|
26
|
+
end
|
|
25
27
|
end
|
|
26
|
-
end
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
private
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
def find_org_id
|
|
32
|
+
orgs = get("/api/orgs")
|
|
33
|
+
orgs&.find { |o| o["name"] == @org }&.fetch("id", nil)
|
|
34
|
+
end
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
def secret_exists?(org_id, secret_name)
|
|
37
|
+
secrets = get("/api/orgs/#{org_id}/secrets")
|
|
38
|
+
secrets&.any? { |s| s["name"] == secret_name }
|
|
39
|
+
end
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
def get(path)
|
|
42
|
+
require "json"
|
|
43
|
+
response = request(Net::HTTP::Get, path)
|
|
44
|
+
JSON.parse(response.body) if response.is_a?(Net::HTTPSuccess)
|
|
45
|
+
end
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
def post(path, body)
|
|
48
|
+
request(Net::HTTP::Post, path, body)
|
|
49
|
+
end
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
def patch(path, body)
|
|
52
|
+
request(Net::HTTP::Patch, path, body)
|
|
53
|
+
end
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
def request(klass, path, body = nil)
|
|
56
|
+
require "net/http"
|
|
57
|
+
require "json"
|
|
58
|
+
require "openssl"
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
uri = URI("#{@server}#{path}")
|
|
61
|
+
http.request(build_req(klass, uri, body))
|
|
62
|
+
end
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
def http
|
|
65
|
+
uri = URI(@server)
|
|
66
|
+
h = Net::HTTP.new(uri.hostname, uri.port)
|
|
67
|
+
h.use_ssl = (uri.scheme == "https")
|
|
68
|
+
h.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
69
|
+
h
|
|
70
|
+
end
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
def build_req(klass, uri, body)
|
|
73
|
+
req = klass.new(uri)
|
|
74
|
+
req["Authorization"] = "Bearer #{@token}"
|
|
75
|
+
if body
|
|
76
|
+
req["Content-Type"] = "application/json"
|
|
77
|
+
req.body = JSON.generate(body)
|
|
78
|
+
end
|
|
79
|
+
req
|
|
77
80
|
end
|
|
78
|
-
req
|
|
79
|
-
end
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
def build_http(uri)
|
|
83
|
+
http = Net::HTTP.new(uri.hostname, uri.port)
|
|
84
|
+
http.use_ssl = (uri.scheme == "https")
|
|
85
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
86
|
+
http
|
|
87
|
+
end
|
|
86
88
|
end
|
|
87
89
|
end
|
|
88
90
|
end
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
$LOAD_PATH.unshift File.join(__dir__, "..", "lib")
|
|
10
10
|
require "rake/gem/maintenance"
|
|
11
11
|
|
|
12
|
-
Rake::
|
|
13
|
-
Rake::
|
|
12
|
+
Rake::Gem::Maintenance::Repos.rubygems_api_key_env_var = "GEM_HOST_API_KEY"
|
|
13
|
+
Rake::Gem::Maintenance::Repos.rubygems_otp_seed_env_var = "RUBYGEMS_OTP_SEED"
|
|
14
14
|
|
|
15
15
|
gemspec_file = Dir["*.gemspec"].first
|
|
16
16
|
abort "ERROR: No gemspec found in #{Dir.pwd}" unless gemspec_file
|
|
@@ -21,7 +21,7 @@ gem_file = Dir["*.gem"].max_by { |f| File.mtime(f) }
|
|
|
21
21
|
abort "ERROR: No .gem file found after build" unless gem_file
|
|
22
22
|
|
|
23
23
|
puts "Publishing #{gem_file} to rubygems.org..."
|
|
24
|
-
publisher = Rake::
|
|
24
|
+
publisher = Rake::Gem::Maintenance::GemPublisher.new(Rake::Gem::Maintenance::Repos.rubygems)
|
|
25
25
|
publisher.publish(gem_file)
|
|
26
26
|
|
|
27
27
|
abort "ERROR: Failed to publish #{gem_file} to rubygems.org" if publisher.successful_repos.empty?
|