Skip to content

Commit d597394

Browse files
committed
Add support for adding a github environment for release action
1 parent 01a37c2 commit d597394

File tree

2 files changed

+95
-101
lines changed

2 files changed

+95
-101
lines changed

.github/workflows/push_gem.yml

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Push Gem
33
on:
44
push:
55
tags:
6-
- v*
6+
- 'v*'
77

88
permissions:
99
contents: read
@@ -13,6 +13,10 @@ jobs:
1313
if: github.repository == 'rubygems/configure_trusted_publisher'
1414
runs-on: ubuntu-latest
1515

16+
environment:
17+
name: rubygems.org
18+
url: https://rubygems.org/gems/configure_trusted_publisher
19+
1620
permissions:
1721
contents: write
1822
id-token: write

lib/configure_trusted_publisher/cli.rb

+90-100
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
require "json"
1010
require "open3"
1111
require "rubygems/gemcutter_utilities"
12+
require "yaml"
1213

1314
Gem.configuration.verbose = true
1415

@@ -153,17 +154,26 @@ def run(repository = ".")
153154
puts "Configuring trusted publisher for #{rubygem_name} in #{File.expand_path(repository)} for " \
154155
"#{github_repository.join('/')}"
155156

157+
write_release_action(repository, rubygem_name, environment: add_environment)
158+
156159
gc = GemcutterUtilities.new(
157160
say: ->(msg) { puts msg },
158-
ask: ->(msg) { ask msg.chomp(":") },
159-
ask_for_password: ->(msg) { ask_secret msg.chomp(":") },
160-
terminate_interaction: ->(msg) { exit msg },
161+
ask: lambda { |msg|
162+
puts
163+
ask msg.chomp(":")
164+
},
165+
ask_for_password: lambda { |msg|
166+
puts
167+
ask_secret msg.chomp(":")
168+
},
169+
terminate_interaction: lambda { |msg|
170+
puts
171+
exit msg
172+
},
161173
otp: options[:otp]
162174
)
163175
gc.sign_in(scope: "configure_trusted_publishers") unless gc.api_key
164176

165-
write_release_action(repository, rubygem_name)
166-
167177
owner, name = github_repository
168178
config = {
169179
"trusted_publisher" => {
@@ -235,13 +245,46 @@ def github_repository
235245
raise "No GitHub repository found for #{gemspec.name}"
236246
end
237247

248+
def add_environment
249+
puts
250+
return unless ask_yes_or_no("Would you like to add a github environment to allow customizing " \
251+
"prerequisites for the action?")
252+
253+
env_name = "rubygems.org"
254+
255+
owner, name = github_repository
256+
puts "Adding GitHub environment to #{owner}/#{name} to protect the action"
257+
if (env = Open3.capture2e("gh", "api", "repos/#{owner}/#{name}/environments").then do |output, status|
258+
exit "Failed to list environments for #{owner}/#{name} using `gh api`:\n#{output}" unless status.success?
259+
260+
JSON.parse(output)["environments"].find { |e| e["name"] == env_name }
261+
end)
262+
263+
puts
264+
puts "Environment 'rubygems.org' already exists for #{owner}/#{name}:\n #{env['html_url']}"
265+
else
266+
Open3.capture2e("gh", "api", "--method", "PUT",
267+
"repos/#{owner}/#{name}/environments/#{env_name}").then do |output, status|
268+
unless status.success?
269+
exit "Failed to create rubygems.org environment for #{owner}/#{name} using `gh api`:\n#{output}"
270+
end
271+
272+
env = JSON.parse(output)
273+
puts
274+
puts "Created environment 'rubygems.org' for #{owner}/#{name}:\n #{env['html_url']}"
275+
end
276+
end
277+
278+
env_name
279+
end
280+
238281
attr_reader :gemspec_source
239282

240283
def gemspec
241284
gemspec_source.specs.first
242285
end
243286

244-
def write_release_action(repository, rubygem_name)
287+
def write_release_action(repository, rubygem_name, environment: nil)
245288
tag = "Automatically when a new tag matching v* is pushed"
246289
manual = "Manually by running a GitHub Action"
247290
puts
@@ -252,103 +295,50 @@ def write_release_action(repository, rubygem_name)
252295
],
253296
default: "2"
254297
)
255-
case response
256-
when tag
257-
write_tag_action(repository)
258-
when manual
259-
write_manual_action(repository)
260-
end
261-
end
262298

263-
def write_tag_action(repository)
264299
action_file = File.expand_path(".github/workflows/push_gem.yml", repository)
265300
return unless check_action(action_file)
266301

267302
File.write(
268303
action_file,
269-
<<~YAML
270-
name: Push Gem
271-
272-
on:
273-
push:
274-
tags:
275-
- v*
276-
277-
permissions:
278-
contents: read
279-
280-
jobs:
281-
push:
282-
if: github.repository == '#{github_repository.join('/')}'
283-
runs-on: ubuntu-latest
284-
285-
permissions:
286-
contents: write
287-
id-token: write
288-
289-
steps:
290-
# Set up
291-
- name: Harden Runner
292-
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
293-
with:
294-
egress-policy: audit
295-
296-
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
297-
- name: Set up Ruby
298-
uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0
299-
with:
300-
bundler-cache: true
301-
ruby-version: ruby
302-
303-
# Release
304-
- uses: rubygems/release-gem@612653d273a73bdae1df8453e090060bb4db5f31 # v1
305-
YAML
306-
)
307-
puts "Created #{action_file}"
308-
end
309-
310-
def write_manual_action(repository)
311-
action_file = File.expand_path(".github/workflows/push_gem.yml", repository)
312-
return unless check_action(action_file)
313-
314-
File.write(
315-
action_file,
316-
<<~YAML
317-
name: Push Gem
318-
319-
on:
320-
workflow_dispatch:
321-
322-
permissions:
323-
contents: read
324-
325-
jobs:
326-
push:
327-
if: github.repository == '#{github_repository.join('/')}'
328-
runs-on: ubuntu-latest
329-
330-
permissions:
331-
contents: write
332-
id-token: write
333-
334-
steps:
335-
# Set up
336-
- name: Harden Runner
337-
uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1
338-
with:
339-
egress-policy: audit
340-
341-
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
342-
- name: Set up Ruby
343-
uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0
344-
with:
345-
bundler-cache: true
346-
ruby-version: ruby
347-
348-
# Release
349-
- uses: rubygems/release-gem@612653d273a73bdae1df8453e090060bb4db5f31 # v1
350-
YAML
351-
304+
[
305+
"name: Push Gem",
306+
nil,
307+
"on:",
308+
" #{response == tag ? "push:\n tags:\n - 'v*'" : 'workflow_dispatch:'}",
309+
nil,
310+
"permissions:",
311+
" contents: read",
312+
nil,
313+
"jobs:",
314+
" push:",
315+
" if: github.repository == '#{github_repository.join('/')}'",
316+
" runs-on: ubuntu-latest",
317+
if environment
318+
"\n environment:\n name: #{environment}\n url: https://rubygems.org/gems/#{rubygem_name}\n"
319+
end,
320+
" permissions:",
321+
" contents: write",
322+
" id-token: write",
323+
nil,
324+
" steps:",
325+
" # Set up",
326+
" - name: Harden Runner",
327+
" uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1",
328+
" with:",
329+
" egress-policy: audit",
330+
nil,
331+
" - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4",
332+
" - name: Set up Ruby",
333+
" uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0",
334+
" with:",
335+
" bundler-cache: true",
336+
" ruby-version: ruby",
337+
nil,
338+
" # Release",
339+
" - uses: rubygems/release-gem@612653d273a73bdae1df8453e090060bb4db5f31 # v1",
340+
nil
341+
].join("\n")
352342
)
353343
puts "Created #{action_file}"
354344
end
@@ -357,9 +347,9 @@ def check_action(action_file)
357347
return FileUtils.mkdir_p(File.dirname(action_file)) || true unless File.exist?(action_file)
358348

359349
puts
360-
response = ask_multiple_choice(
361-
"#{action_file} already exists, overwrite?", { "y" => "Yes", "n" => "No" },
362-
default: "n"
350+
response = ask_yes_or_no(
351+
"#{action_file} already exists, overwrite?",
352+
default: false
363353
)
364354
return if response == "No"
365355

0 commit comments

Comments
 (0)