bake-gem-github 0.5.0 → 0.6.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
- checksums.yaml.gz.sig +0 -0
- data/bake/gem/github/setup.rb +22 -2
- data/context/getting-started.md +18 -2
- data/lib/bake/gem/github/project.rb +12 -6
- data/lib/bake/gem/github/trusted_publisher.rb +124 -0
- data/lib/bake/gem/github/version.rb +1 -1
- data/readme.md +5 -0
- data/releases.md +5 -0
- data.tar.gz.sig +0 -0
- metadata +2 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 46e9578ab60761af87b7130ba3642b00401e89febdcd7e8cced97d74252e37b6
|
|
4
|
+
data.tar.gz: 53997e6e60991e6e5b057cd62ee7897de5156306d4dceb3d8a6bef2a269cd349
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bef7899e4fbd943cd791a72562739f04e8831f5c1fbc7b4760426c48bb9b99bf6fb96d931de8399423203d147b6775c65f137392b9e55ae7761fcb733aa00cb8
|
|
7
|
+
data.tar.gz: 28a9ccd6198bb07906119a6b89cf0fa23874756a5dcc53d335032bb94eebc5843b79b3f843a4ac5cfb6c911c7fd372a52095e9618d37b2bc5820c7ef463ff8c0
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/bake/gem/github/setup.rb
CHANGED
|
@@ -4,9 +4,29 @@
|
|
|
4
4
|
# Copyright, 2026, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
# Inspect external settings before applying the generated policy.
|
|
7
|
+
# @parameter publisher [Boolean] Authenticate to RubyGems and check the expected publisher registration.
|
|
8
|
+
# @parameter key [Symbol] An explicitly selected RubyGems API key name.
|
|
9
|
+
# @parameter otp [String] An optional RubyGems MFA code.
|
|
7
10
|
# @returns [Hash] Desired and observed settings for review.
|
|
8
|
-
def plan
|
|
9
|
-
context.lookup("gem:github:doctor").call
|
|
11
|
+
def plan(publisher: false, key: nil, otp: nil)
|
|
12
|
+
result = context.lookup("gem:github:doctor").call
|
|
13
|
+
if publisher
|
|
14
|
+
require "bake/gem/github/trusted_publisher"
|
|
15
|
+
|
|
16
|
+
result[:trusted_publisher_status] = Bake::Gem::GitHub::TrustedPublisher.load(context.root, key: key, otp: otp).status
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
return result
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Register the configured release workflow as a trusted publisher for an existing gem on RubyGems.org.
|
|
23
|
+
# @parameter key [Symbol] An explicitly selected RubyGems API key name.
|
|
24
|
+
# @parameter otp [String] An optional RubyGems MFA code.
|
|
25
|
+
# @returns [Hash] Whether the publisher was created and its API record.
|
|
26
|
+
def publisher(key: nil, otp: nil)
|
|
27
|
+
require "bake/gem/github/trusted_publisher"
|
|
28
|
+
|
|
29
|
+
Bake::Gem::GitHub::TrustedPublisher.load(context.root, key: key, otp: otp).register
|
|
10
30
|
end
|
|
11
31
|
|
|
12
32
|
# Apply the four managed rulesets and configured environment reviewers using the current gh administrator credentials.
|
data/context/getting-started.md
CHANGED
|
@@ -48,9 +48,25 @@ The rules require two approvals by default, allow explicit administrator bypass,
|
|
|
48
48
|
|
|
49
49
|
## Configure publishing credentials
|
|
50
50
|
|
|
51
|
-
Create a `rubygems` GitHub environment restricted to the default branch.
|
|
51
|
+
Create a `rubygems` GitHub environment restricted to the default branch. For an existing gem on RubyGems.org, run this task from the repository root as a gem owner:
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
``` bash
|
|
54
|
+
bundle exec bake gem:github:setup:publisher
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The task reads the gem name from its gemspec and the owner/repository and environment from `config/release.yaml`. It registers the **`release-publish.yaml`** workflow with the configured environment (**`rubygems`** by default). An exact existing match is reused, so reruns do not create duplicate registrations. Other publishers are preserved; the task does not remove or replace them.
|
|
58
|
+
|
|
59
|
+
Without an explicitly supplied API key, the task prompts for RubyGems credentials and any required MFA verification. It requests a key with only the `configure_trusted_publishers` scope, expiring after 15 minutes, and keeps it in memory. It does not read or overwrite your default saved push key. To use an existing key with this scope, supply `GEM_HOST_API_KEY` or select a named RubyGems credential with `key=publisher`. Registration and the optional plan check also accept `otp=123456` or `GEM_HOST_OTP_CODE` when needed.
|
|
60
|
+
|
|
61
|
+
To check registration without creating a publisher:
|
|
62
|
+
|
|
63
|
+
``` bash
|
|
64
|
+
bundle exec bake gem:github:setup:plan publisher=true
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This adds `trusted_publisher_status` to the plan, including `configured` and the matching registration. The default plan only reports expected RubyGems values and does not authenticate to RubyGems. Authentication, ownership, and API failures stop the task rather than being reported as a missing registration.
|
|
68
|
+
|
|
69
|
+
For a gem that has never been published, configure a pending publisher manually. Account ownership, MFA enrollment, the GitHub environment, and signing bootstrap remain manual setup steps. See [RubyGems Trusted Publishing](https://guides.rubygems.org/trusted-publishing/) for account setup. Trusted Publishing supplies the publishing credential for each release, so a long-lived RubyGems API key is unnecessary.
|
|
54
70
|
|
|
55
71
|
When `release.cert` exists, setup enables certificate signing. Commit the public certificate and install its matching private key as `GEM_SIGNING_KEY`, either in the `rubygems` environment or as an organization secret available to the repository. The publisher checks certificate validity, key matching, and package signatures. Use `signing=false` during setup to disable certificate signing.
|
|
56
72
|
|
|
@@ -181,12 +181,18 @@ module Bake
|
|
|
181
181
|
existing_rules: api("rulesets?per_page=100"),
|
|
182
182
|
environments: api("environments"),
|
|
183
183
|
environment_changes: environment_changes,
|
|
184
|
-
trusted_publisher:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
184
|
+
trusted_publisher: trusted_publisher,
|
|
185
|
+
}
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Describe the RubyGems publisher for the reviewed release workflow.
|
|
189
|
+
# @returns [Hash] Repository, workflow filename, and environment settings.
|
|
190
|
+
def trusted_publisher
|
|
191
|
+
{
|
|
192
|
+
repository_owner: @repository.split("/").first,
|
|
193
|
+
repository_name: @repository.split("/").last,
|
|
194
|
+
workflow_filename: "release-publish.yaml",
|
|
195
|
+
environment: @config.fetch("environment"),
|
|
190
196
|
}
|
|
191
197
|
end
|
|
192
198
|
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "json"
|
|
7
|
+
require "rubygems/gemcutter_utilities"
|
|
8
|
+
require "rubygems/user_interaction"
|
|
9
|
+
require "uri"
|
|
10
|
+
require_relative "project"
|
|
11
|
+
|
|
12
|
+
module Bake
|
|
13
|
+
module Gem
|
|
14
|
+
module GitHub
|
|
15
|
+
# Registers the reviewed release workflow with RubyGems.org using its owner API.
|
|
16
|
+
class TrustedPublisher
|
|
17
|
+
include ::Gem::UserInteraction
|
|
18
|
+
include ::Gem::GemcutterUtilities
|
|
19
|
+
|
|
20
|
+
# The RubyGems API scope for managing trusted publishers.
|
|
21
|
+
SCOPE = :configure_trusted_publishers
|
|
22
|
+
|
|
23
|
+
# The provider type accepted by the RubyGems trusted publisher API.
|
|
24
|
+
TYPE = "OIDC::TrustedPublisher::GitHubAction"
|
|
25
|
+
|
|
26
|
+
# Load the gem identity and reviewed publisher settings from the current project.
|
|
27
|
+
# @parameter root [String] The repository root, which must also be the working directory.
|
|
28
|
+
# @parameter options [Hash] Authentication options passed to {initialize}.
|
|
29
|
+
# @returns [TrustedPublisher] The configured RubyGems client.
|
|
30
|
+
def self.load(root, **options)
|
|
31
|
+
project = Project.new(root)
|
|
32
|
+
name = Helper.new(root).gemspec.name
|
|
33
|
+
|
|
34
|
+
new(name, project.trusted_publisher, **options)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Configure an existing gem's expected GitHub Actions publisher.
|
|
38
|
+
# @parameter name [String] The gem name.
|
|
39
|
+
# @parameter settings [Hash] Repository owner/name, workflow filename, and environment.
|
|
40
|
+
# @parameter key [Symbol | Nil] An explicitly selected key in RubyGems credentials.
|
|
41
|
+
# @parameter otp [String | Nil] An optional MFA code; RubyGems can prompt when required.
|
|
42
|
+
def initialize(name, settings, key: nil, otp: nil)
|
|
43
|
+
@path = "api/v1/gems/#{URI.encode_www_form_component(name)}/trusted_publishers"
|
|
44
|
+
@settings = settings.transform_keys(&:to_s)
|
|
45
|
+
@options = {key: key, otp: otp}
|
|
46
|
+
@host = "https://rubygems.org"
|
|
47
|
+
@api_key = nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @attribute [Hash] Options used by RubyGems authentication and MFA support.
|
|
51
|
+
attr_reader :options
|
|
52
|
+
|
|
53
|
+
# Read the matching publisher without changing publisher configuration.
|
|
54
|
+
# @returns [Hash] Whether the publisher is configured and its existing API record, if any.
|
|
55
|
+
# @raises [RuntimeError] If RubyGems cannot list the gem's publishers.
|
|
56
|
+
def status
|
|
57
|
+
sign_in(scope: SCOPE) unless api_key
|
|
58
|
+
publishers = request(:get, expected: "200")
|
|
59
|
+
raise "Invalid RubyGems trusted publishers response." unless publishers.is_a?(Array)
|
|
60
|
+
publisher = publishers.find{|record| matching?(record)}
|
|
61
|
+
|
|
62
|
+
return {configured: !publisher.nil?, publisher: publisher}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Register a missing publisher, preserving all existing publisher registrations.
|
|
66
|
+
# @returns [Hash] Whether registration was created and the matching API record.
|
|
67
|
+
# @raises [RuntimeError] If RubyGems rejects registration or returns different settings.
|
|
68
|
+
def register
|
|
69
|
+
current = status
|
|
70
|
+
return {created: false, publisher: current.fetch(:publisher)} if current.fetch(:configured)
|
|
71
|
+
|
|
72
|
+
publisher = request(:post, expected: "201", payload: {trusted_publisher_type: TYPE, trusted_publisher: @settings})
|
|
73
|
+
raise "RubyGems returned unexpected trusted publisher settings." unless matching?(publisher)
|
|
74
|
+
|
|
75
|
+
return {created: true, publisher: publisher}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Use the session key or an explicitly supplied credential, leaving the default push key alone.
|
|
79
|
+
# @returns [String | Nil] The API key used for this session.
|
|
80
|
+
def api_key
|
|
81
|
+
@api_key || ENV["GEM_HOST_API_KEY"] || (options[:key] && verify_api_key(options[:key]))
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Keep newly issued credentials in memory rather than changing the user's credentials file.
|
|
85
|
+
# @parameter host [String] The authenticated RubyGems host.
|
|
86
|
+
# @parameter key [String] The newly issued API key.
|
|
87
|
+
def set_api_key(host, key)
|
|
88
|
+
@api_key = key
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def get_mfa_params(profile)
|
|
94
|
+
super.merge(expires_at: (Time.now.utc + 900).strftime("%Y-%m-%d %H:%M:%S UTC"))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def matching?(record)
|
|
98
|
+
return false unless record.fetch("trusted_publisher_type") == TYPE
|
|
99
|
+
settings = record.fetch("trusted_publisher")
|
|
100
|
+
|
|
101
|
+
# Generated publishing workflows run in the configured repository, not a reusable workflow repository:
|
|
102
|
+
@settings.all?{|key, value| settings[key] == value} &&
|
|
103
|
+
settings["workflow_repository_owner"].nil? && settings["workflow_repository_name"].nil?
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def request(method, expected:, payload: nil)
|
|
107
|
+
response = rubygems_api_request(method, @path, scope: SCOPE) do |request|
|
|
108
|
+
request["Authorization"] = api_key
|
|
109
|
+
request["Accept"] = "application/json"
|
|
110
|
+
if payload
|
|
111
|
+
request["Content-Type"] = "application/json"
|
|
112
|
+
request.body = JSON.generate(payload)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
unless response.code == expected
|
|
116
|
+
raise "RubyGems trusted publisher #{method.to_s.upcase} failed (HTTP #{response.code}): #{clean_text(response.body)}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
JSON.parse(response.body)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
data/readme.md
CHANGED
|
@@ -24,6 +24,11 @@ Please see the [project documentation](https://socketry.github.io/bake-gem-githu
|
|
|
24
24
|
|
|
25
25
|
Please see the [project releases](https://socketry.github.io/bake-gem-github/releases/index) for all releases.
|
|
26
26
|
|
|
27
|
+
### v0.6.0
|
|
28
|
+
|
|
29
|
+
- Register RubyGems trusted publishers from the gemspec and release configuration with `gem:github:setup:publisher`, reusing existing registrations and RubyGems authentication/MFA support.
|
|
30
|
+
- Check publisher registration explicitly with `gem:github:setup:plan publisher=true`.
|
|
31
|
+
|
|
27
32
|
### v0.5.0
|
|
28
33
|
|
|
29
34
|
- Support rebase merging by requiring each release PR to contain exactly one commit, while leaving ordinary PRs unrestricted. Generated rules allow merge, squash, and rebase methods according to repository settings.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.6.0
|
|
4
|
+
|
|
5
|
+
- Register RubyGems trusted publishers from the gemspec and release configuration with `gem:github:setup:publisher`, reusing existing registrations and RubyGems authentication/MFA support.
|
|
6
|
+
- Check publisher registration explicitly with `gem:github:setup:plan publisher=true`.
|
|
7
|
+
|
|
3
8
|
## v0.5.0
|
|
4
9
|
|
|
5
10
|
- Support rebase merging by requiring each release PR to contain exactly one commit, while leaving ordinary PRs unrestricted. Generated rules allow merge, squash, and rebase methods according to repository settings.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bake-gem-github
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -107,6 +107,7 @@ files:
|
|
|
107
107
|
- lib/bake/gem/github/publisher.rb
|
|
108
108
|
- lib/bake/gem/github/registry.rb
|
|
109
109
|
- lib/bake/gem/github/setup.rb
|
|
110
|
+
- lib/bake/gem/github/trusted_publisher.rb
|
|
110
111
|
- lib/bake/gem/github/version.rb
|
|
111
112
|
- license.md
|
|
112
113
|
- readme.md
|
metadata.gz.sig
CHANGED
|
Binary file
|