hiiro 0.1.118 → 0.1.119

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/h-pr +161 -26
  3. data/lib/hiiro/version.rb +1 -1
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d5db5d7bc2e807d5854c665238e5363287b3f233392d490d34b3d08dcd8e97f
4
- data.tar.gz: 9a7de1d509b867a1ea0e9ef168ad28ae114daf4fc580f15a2f9af35f4beca0da
3
+ metadata.gz: ef6f36b1463e9110cdcad7f2ad04b326723ede4dfa271d750330c7ab7f38471f
4
+ data.tar.gz: 5f1e701987e8ff6e88cea4a43867a808f524e81dba16aa116a6e4b3f2ce5d443
5
5
  SHA512:
6
- metadata.gz: 339d17f8c1f2b90e258db099b3b27d93f680043f0602716c550f7c01b11aa4693430fc543b81ff0695dcacb0750932cc107814641d6a72cbbc075bd99e6ebeb2
7
- data.tar.gz: c0d41b5d20368486f92376ab5327fa5d6e0c206d4aab5371c3666c7045f0a940a0ff0fbd2fd1dec50ee494eecb8be0b42cd7dede31663eab6137352aaaf1312b
6
+ metadata.gz: 80a32fa06977df7449b4dea9fa3465278f3b9dd167a29fbe247e92de96d0c4633f339f86e5e1316100767db333b8f032fc7da05162482eaea2eeb3270c2e4450
7
+ data.tar.gz: 455f0737f65575f197aafbbe5656ae62b23a4a0c270ca090c3c89531b990d4945a1f6cda40f19a40accc0dc64fb1e2fec92ec63d76b04042e71a019dab35b09e
data/bin/h-pr CHANGED
@@ -16,16 +16,6 @@ class PRManager
16
16
  @hiiro = hiiro
17
17
  end
18
18
 
19
- def help
20
- puts "Usage: h pr <subcommand> [args]"
21
- puts
22
- puts "Subcommands:"
23
- puts " save [PR_NUMBER] Record PR for this task (auto-detects if omitted)"
24
- puts " current Show current branch's PR info"
25
- puts " open [PR_NUMBER] Open PR in browser"
26
- puts " view [PR_NUMBER] View PR details in terminal"
27
- end
28
-
29
19
  def save(pr_number = nil)
30
20
  pr_info = fetch_pr_info(pr_number)
31
21
  unless pr_info
@@ -266,7 +256,130 @@ class PinnedPRManager
266
256
  (authored_prs + assigned_prs).uniq { |pr| pr['number'] }
267
257
  end
268
258
 
269
- def refresh_status(pr)
259
+ def needs_refresh?(pr, force: false)
260
+ return true if force
261
+ return true unless pr['last_checked']
262
+
263
+ last_check_time = Time.parse(pr['last_checked']) rescue nil
264
+ return true unless last_check_time
265
+
266
+ # Refresh if last check was more than 2 minutes ago
267
+ (Time.now - last_check_time) > 120
268
+
269
+
270
+ def batch_fetch_pr_info(pr_numbers)
271
+ return {} if pr_numbers.empty?
272
+
273
+ # Build GraphQL query to fetch multiple PRs at once
274
+ pr_queries = pr_numbers.map.with_index do |num, idx|
275
+ <<~GRAPHQL.strip
276
+ pr#{idx}: pullRequest(number: #{num}) {
277
+ number
278
+ title
279
+ url
280
+ headRefName
281
+ state
282
+ isDraft
283
+ mergeable
284
+ reviewDecision
285
+ statusCheckRollup {
286
+ contexts {
287
+ ... on CheckRun {
288
+ conclusion
289
+ status
290
+ }
291
+ ... on StatusContext {
292
+ state
293
+ }
294
+ }
295
+ }
296
+ reviews(last: 50) {
297
+ nodes {
298
+ author {
299
+ login
300
+ }
301
+ state
302
+ }
303
+ }
304
+ }
305
+ GRAPHQL
306
+ end
307
+
308
+ query = <<~GRAPHQL
309
+ query {
310
+ repository(owner: "instacart", name: "carrot") {
311
+ #{pr_queries.join("\n")}
312
+ }
313
+ }
314
+ GRAPHQL
315
+
316
+ result = `gh api graphql -f query='#{query.gsub("'", "'\\''")}' 2>/dev/null`
317
+ return {} if result.empty?
318
+
319
+ data = JSON.parse(result)
320
+ repo_data = data.dig('data', 'repository')
321
+ return {} unless repo_data
322
+
323
+ # Convert GraphQL response to hash keyed by PR number
324
+ pr_info_by_number = {}
325
+ pr_numbers.each_with_index do |num, idx|
326
+ pr_data = repo_data["pr#{idx}"]
327
+ next unless pr_data
328
+
329
+ # Transform GraphQL response to match gh pr view format
330
+ pr_info_by_number[num] = {
331
+ 'number' => pr_data['number'],
332
+ 'title' => pr_data['title'],
333
+ 'url' => pr_data['url'],
334
+ 'headRefName' => pr_data['headRefName'],
335
+ 'state' => pr_data['state'],
336
+ 'isDraft' => pr_data['isDraft'],
337
+ 'mergeable' => pr_data['mergeable'],
338
+ 'reviewDecision' => pr_data['reviewDecision'],
339
+ 'statusCheckRollup' => pr_data['statusCheckRollup'],
340
+ 'reviews' => pr_data.dig('reviews', 'nodes') || []
341
+ }
342
+ end
343
+
344
+ pr_info_by_number
345
+ rescue JSON::ParserError, StandardError
346
+ {}
347
+ end
348
+
349
+ def refresh_all_status(prs, force: false)
350
+ # Determine which PRs need refreshing
351
+ prs_to_refresh = prs.select { |pr| needs_refresh?(pr, force: force) }
352
+
353
+ if prs_to_refresh.empty?
354
+ puts "All PRs recently checked (within last 2 minutes). Use -U to force update." unless force
355
+ return prs
356
+ end
357
+
358
+ # Batch fetch all PR info at once
359
+ pr_numbers = prs_to_refresh.map { |pr| pr['number'] }
360
+ fetched_info = batch_fetch_pr_info(pr_numbers)
361
+
362
+ # Update each PR with fetched info
363
+ prs_to_refresh.each do |pr|
364
+ info = fetched_info[pr['number']]
365
+ next unless info
366
+
367
+ pr['state'] = info['state']
368
+ pr['title'] = info['title']
369
+ pr['checks'] = summarize_checks(info['statusCheckRollup'])
370
+ pr['reviews'] = summarize_reviews(info['reviews'])
371
+ pr['review_decision'] = info['reviewDecision']
372
+ pr['is_draft'] = info['isDraft']
373
+ pr['mergeable'] = info['mergeable']
374
+ pr['last_checked'] = Time.now.iso8601
375
+ end
376
+
377
+ prs
378
+ end
379
+
380
+ def refresh_status(pr, force: false)
381
+ return pr unless needs_refresh?(pr, force: force)
382
+
270
383
  info = fetch_pr_info(pr['number'])
271
384
  return pr unless info
272
385
 
@@ -661,12 +774,14 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
661
774
  end
662
775
 
663
776
  compact = status_args.include?('-c') || status_args.include?('--compact')
777
+ force = status_args.include?('-U') || status_args.include?('--force-update')
664
778
 
665
779
  puts "Refreshing status for #{pinned.length} pinned PR(s)..."
666
780
  puts
667
781
 
782
+ pinned_manager.refresh_all_status(pinned, force: force)
783
+
668
784
  pinned.each_with_index do |pr, idx|
669
- pinned_manager.refresh_status(pr)
670
785
  if compact
671
786
  puts pinned_manager.display_pinned(pr, idx)
672
787
  else
@@ -680,7 +795,7 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
680
795
  puts "Status updated at #{Time.now.strftime('%H:%M:%S')}"
681
796
  end
682
797
 
683
- add_subcmd(:update) do
798
+ add_subcmd(:update) do |*update_args|
684
799
  pinned = pinned_manager.load_pinned
685
800
 
686
801
  if pinned.empty?
@@ -688,15 +803,19 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
688
803
  next
689
804
  end
690
805
 
806
+ force = update_args.include?('-U') || update_args.include?('--force-update')
807
+
691
808
  puts "Updating status for #{pinned.length} PR(s)..."
692
- pinned.each { |pr| pinned_manager.refresh_status(pr) }
809
+ pinned_manager.refresh_all_status(pinned, force: force)
693
810
  pinned_manager.save_pinned(pinned)
694
811
  puts "Done."
695
812
  end
696
813
 
697
- add_subcmd(:green) do
814
+ add_subcmd(:green) do |*green_args|
698
815
  pinned = pinned_manager.load_pinned
699
- pinned.each { |pr| pinned_manager.refresh_status(pr) }
816
+ force = green_args.include?('-U') || green_args.include?('--force-update')
817
+
818
+ pinned_manager.refresh_all_status(pinned, force: force)
700
819
  pinned_manager.save_pinned(pinned)
701
820
 
702
821
  filtered = pinned.select { |pr|
@@ -714,9 +833,11 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
714
833
  filtered.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
715
834
  end
716
835
 
717
- add_subcmd(:red) do
836
+ add_subcmd(:red) do |*red_args|
718
837
  pinned = pinned_manager.load_pinned
719
- pinned.each { |pr| pinned_manager.refresh_status(pr) }
838
+ force = red_args.include?('-U') || red_args.include?('--force-update')
839
+
840
+ pinned_manager.refresh_all_status(pinned, force: force)
720
841
  pinned_manager.save_pinned(pinned)
721
842
 
722
843
  filtered = pinned.select { |pr|
@@ -734,9 +855,11 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
734
855
  filtered.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
735
856
  end
736
857
 
737
- add_subcmd(:old) do
858
+ add_subcmd(:old) do |*old_args|
738
859
  pinned = pinned_manager.load_pinned
739
- pinned.each { |pr| pinned_manager.refresh_status(pr) }
860
+ force = old_args.include?('-U') || old_args.include?('--force-update')
861
+
862
+ pinned_manager.refresh_all_status(pinned, force: force)
740
863
  pinned_manager.save_pinned(pinned)
741
864
 
742
865
  filtered = pinned.select { |pr| pr['state'] == 'MERGED' }
@@ -760,7 +883,8 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
760
883
  next
761
884
  end
762
885
 
763
- pinned.each { |pr| pinned_manager.refresh_status(pr) }
886
+ force = args.include?('-U') || args.include?('--force-update')
887
+ pinned_manager.refresh_all_status(pinned, force: force)
764
888
 
765
889
  merged_or_closed = pinned.select { |pr| pr['state'] == 'MERGED' || pr['state'] == 'CLOSED' }
766
890
 
@@ -782,9 +906,11 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
782
906
  puts "Done. #{pinned_manager.load_pinned.length} PR(s) still tracked."
783
907
  end
784
908
 
785
- add_subcmd(:draft) do
909
+ add_subcmd(:draft) do |*draft_args|
786
910
  pinned = pinned_manager.load_pinned
787
- pinned.each { |pr| pinned_manager.refresh_status(pr) }
911
+ force = draft_args.include?('-U') || draft_args.include?('--force-update')
912
+
913
+ pinned_manager.refresh_all_status(pinned, force: force)
788
914
  pinned_manager.save_pinned(pinned)
789
915
 
790
916
  filtered = pinned.select { |pr| pr['is_draft'] == true }
@@ -827,7 +953,14 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
827
953
  missing_numbers = missing.map { |pr| pr['number'] }
828
954
 
829
955
  tmpfile = Tempfile.new(['missing-prs-', '.yml'])
830
- tmpfile.write(missing_numbers.to_yaml)
956
+ lines = ['---']
957
+ missing.each do |pr|
958
+ lines << ''
959
+ lines << "# [#{pr['headRefName']}] #{pr['title']}"
960
+ lines << "# url: #{pr['url']}"
961
+ lines << "- #{pr['number']}"
962
+ end
963
+ tmpfile.write(lines.join("\n"))
831
964
  tmpfile.close
832
965
 
833
966
  system(ENV['EDITOR'] || 'nvim', tmpfile.path)
@@ -944,7 +1077,8 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
944
1077
  end
945
1078
 
946
1079
  # Refresh all statuses first
947
- pinned.each { |pr| pinned_manager.refresh_status(pr) }
1080
+ force = args.include?('-U') || args.include?('--force-update')
1081
+ pinned_manager.refresh_all_status(pinned, force: force)
948
1082
  pinned_manager.save_pinned(pinned)
949
1083
 
950
1084
  merged = pinned.select { |pr| pr['state'] == 'MERGED' }
@@ -975,7 +1109,8 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
975
1109
  end
976
1110
 
977
1111
  # Refresh all statuses first
978
- pinned.each { |pr| pinned_manager.refresh_status(pr) }
1112
+ force = args.include?('-U') || args.include?('--force-update')
1113
+ pinned_manager.refresh_all_status(pinned, force: force)
979
1114
 
980
1115
  merged_or_closed = pinned.select { |pr| pr['state'] == 'MERGED' || pr['state'] == 'CLOSED' }
981
1116
 
data/lib/hiiro/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Hiiro
2
- VERSION = "0.1.118"
2
+ VERSION = "0.1.119"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hiiro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.118
4
+ version: 0.1.119
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Toyota