9
9
require "json"
10
10
require "open3"
11
11
require "rubygems/gemcutter_utilities"
12
+ require "yaml"
12
13
13
14
Gem . configuration . verbose = true
14
15
@@ -153,17 +154,26 @@ def run(repository = ".")
153
154
puts "Configuring trusted publisher for #{ rubygem_name } in #{ File . expand_path ( repository ) } for " \
154
155
"#{ github_repository . join ( '/' ) } "
155
156
157
+ write_release_action ( repository , rubygem_name , environment : add_environment )
158
+
156
159
gc = GemcutterUtilities . new (
157
160
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
+ } ,
161
173
otp : options [ :otp ]
162
174
)
163
175
gc . sign_in ( scope : "configure_trusted_publishers" ) unless gc . api_key
164
176
165
- write_release_action ( repository , rubygem_name )
166
-
167
177
owner , name = github_repository
168
178
config = {
169
179
"trusted_publisher" => {
@@ -235,13 +245,46 @@ def github_repository
235
245
raise "No GitHub repository found for #{ gemspec . name } "
236
246
end
237
247
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
+
238
281
attr_reader :gemspec_source
239
282
240
283
def gemspec
241
284
gemspec_source . specs . first
242
285
end
243
286
244
- def write_release_action ( repository , rubygem_name )
287
+ def write_release_action ( repository , rubygem_name , environment : nil )
245
288
tag = "Automatically when a new tag matching v* is pushed"
246
289
manual = "Manually by running a GitHub Action"
247
290
puts
@@ -252,103 +295,50 @@ def write_release_action(repository, rubygem_name)
252
295
] ,
253
296
default : "2"
254
297
)
255
- case response
256
- when tag
257
- write_tag_action ( repository )
258
- when manual
259
- write_manual_action ( repository )
260
- end
261
- end
262
298
263
- def write_tag_action ( repository )
264
299
action_file = File . expand_path ( ".github/workflows/push_gem.yml" , repository )
265
300
return unless check_action ( action_file )
266
301
267
302
File . write (
268
303
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 " )
352
342
)
353
343
puts "Created #{ action_file } "
354
344
end
@@ -357,9 +347,9 @@ def check_action(action_file)
357
347
return FileUtils . mkdir_p ( File . dirname ( action_file ) ) || true unless File . exist? ( action_file )
358
348
359
349
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
363
353
)
364
354
return if response == "No"
365
355
0 commit comments