Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attest by default when using trusted publishing #11

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ inputs:
description: "Whether to setup the trusted publisher for the gem"
required: false
default: "true"
attestations:
description: >-
[EXPERIMENTAL]
Enable experimental support for sigstore attestations.
Only works with RubyGems.org via Trusted Publishing.
required: false
default: "true"
outputs: {}
branding:
color: "red"
Expand All @@ -25,10 +32,12 @@ runs:
shell: bash
- name: Configure trusted publishing credentials
if: ${{ inputs.setup-trusted-publisher == 'true' }}
uses: rubygems/configure-rubygems-credentials@v1.0.0
uses: rubygems/configure-rubygems-credentials@a2b9242bc411d79356771fc9b9ddebcc3cd1b5dd
segiddins marked this conversation as resolved.
Show resolved Hide resolved
- name: Run release rake task
run: bundle exec rake release
shell: bash
env:
RUBYOPT: "${{ inputs.attestations == 'true' && format('-r{0}/rubygems-attestation-patch.rb {1}', github.action_path, env.RUBYOPT) || env.RUBYOPT }}"
- name: Wait for release to propagate
if: ${{ inputs.await-release == 'true' }}
run: gem exec rubygems-await pkg/*.gem
Expand Down
49 changes: 49 additions & 0 deletions rubygems-attestation-patch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

return unless defined?(Gem)

require "rubygems/commands/push_command"

Gem::Commands::PushCommand.prepend(Module.new do
def send_push_request(name, args)
return super if options[:attestations]&.any? || @host != "https://rubygems.org"

begin
send_push_request_with_attestation(name, args)
rescue StandardError => e
alert_warning "Failed to push with attestation, retrying without attestation.\n#{e.full_message}"
super
end
end

def send_push_request_with_attestation(name, args)
attestation = attest!(name)
if options[:attestations]
options[:attestations] << attestation
send_push_request(name, args)
else
rubygems_api_request(*args, scope: get_push_scope) do |request|
request.set_form([
["gem", Gem.read_binary(name), { filename: name, content_type: "application/octet-stream" }],
["attestations", "[#{Gem.read_binary(attestation)}]", { content_type: "application/json" }]
], "multipart/form-data")
request.add_field "Authorization", api_key
end
end
end

def attest!(name)
require "open3"
bundle = "#{name}.sigstore.json"
env = defined?(Bundler.unbundled_env) ? Bundler.unbundled_env : ENV.to_h
out, st = Open3.capture2e(
env,
Gem.ruby, "-S", "gem", "exec",
"sigstore-cli:0.2.1", "sign", name, "--bundle", bundle,
unsetenv_others: true
)
raise Gem::Exception, "Failed to sign gem:\n\n#{out}" unless st.success?

bundle
end
end)