hiiro 0.1.233 → 0.1.235
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
- data/bin/h-pr +263 -56
- data/lib/hiiro/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '082d552b0f5f6b39721147e1f6bfd2967c8119ce2e46d544f7567c1dc60d26e2'
|
|
4
|
+
data.tar.gz: 64658ea73dbf23e723ad51ec3c0b68b9a7452c8d33a3402c97bd77427089f21a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1677a7f3648f91d7ad6345d1e04f80684f7869763adb313f6fb47320b4f8f43c9abdf7678c5493ded5e990c91bd8131420183c7c50300537cfc64fa412fb4e92
|
|
7
|
+
data.tar.gz: 5d48770e81a3a8e44080a8dd5afd411f8891a146730d94f0c8db921bc0a75016bbe88b1b36f3fa9b9597ccaa32d20de3001836378d119d2f3f1dc6ff2fb60fd6
|
data/bin/h-pr
CHANGED
|
@@ -288,6 +288,16 @@ class PinnedPRManager
|
|
|
288
288
|
|
|
289
289
|
FAILED_CONCLUSIONS = %w[FAILURE ERROR TIMED_OUT STALE STARTUP_FAILURE ACTION_REQUIRED].freeze
|
|
290
290
|
|
|
291
|
+
FILTER_PREDICATES = {
|
|
292
|
+
red: ->(pr) { (c = pr['checks']) && c['failed'].to_i > 0 },
|
|
293
|
+
green: ->(pr) { (c = pr['checks']) && c['failed'].to_i == 0 && c['pending'].to_i == 0 && c['success'].to_i > 0 },
|
|
294
|
+
conflicts: ->(pr) { pr['mergeable'] == 'CONFLICTING' },
|
|
295
|
+
drafts: ->(pr) { pr['is_draft'] == true },
|
|
296
|
+
pending: ->(pr) { (c = pr['checks']) && c['pending'].to_i > 0 && c['failed'].to_i == 0 },
|
|
297
|
+
merged: ->(pr) { pr['state'] == 'MERGED' },
|
|
298
|
+
active: ->(pr) { pr['state'] != 'MERGED' && pr['state'] != 'CLOSED' },
|
|
299
|
+
}.freeze
|
|
300
|
+
|
|
291
301
|
def summarize_checks(rollup)
|
|
292
302
|
return nil unless rollup
|
|
293
303
|
|
|
@@ -368,7 +378,7 @@ class PinnedPRManager
|
|
|
368
378
|
|
|
369
379
|
as_str = as > 0 ? "\e[30;102m#{as}\e[0m" : '-'
|
|
370
380
|
crs_str = crs > 0 ? "\e[30;103m#{crs}\e[0m" : '-'
|
|
371
|
-
conflict_str = pr['mergeable'] == 'CONFLICTING' ? " \e[30;
|
|
381
|
+
conflict_str = pr['mergeable'] == 'CONFLICTING' ? " \e[30;101mC\e[0m" : ""
|
|
372
382
|
reviews_str = "#{as_str}a/#{crs_str}cr#{conflict_str}"
|
|
373
383
|
|
|
374
384
|
repo = pr_repo(pr)
|
|
@@ -377,6 +387,17 @@ class PinnedPRManager
|
|
|
377
387
|
"#{num} #{state_icon} #{reviews_str} ##{pr['number']}#{repo_label} #{pr['title']}".strip
|
|
378
388
|
end
|
|
379
389
|
|
|
390
|
+
def filter_active?(opts)
|
|
391
|
+
FILTER_PREDICATES.keys.any? { |f| opts.respond_to?(f) && opts.send(f) }
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def apply_filters(prs, opts, forced: [])
|
|
395
|
+
active = FILTER_PREDICATES.keys.select { |f| opts.respond_to?(f) && opts.send(f) }
|
|
396
|
+
active = (active + forced).uniq
|
|
397
|
+
return prs if active.empty?
|
|
398
|
+
prs.select { |pr| active.any? { |f| FILTER_PREDICATES[f]&.call(pr) } }
|
|
399
|
+
end
|
|
400
|
+
|
|
380
401
|
def display_detailed(pr, idx = nil)
|
|
381
402
|
lines = []
|
|
382
403
|
num = idx ? "#{idx + 1}." : ""
|
|
@@ -446,6 +467,17 @@ class PinnedPRManager
|
|
|
446
467
|
end
|
|
447
468
|
end
|
|
448
469
|
|
|
470
|
+
FILTER_OPTS = Proc.new {
|
|
471
|
+
flag(:red, short: 'r', desc: 'filter: failing checks')
|
|
472
|
+
flag(:green, short: 'g', desc: 'filter: passing checks')
|
|
473
|
+
flag(:conflicts, short: 'c', desc: 'filter: merge conflicts')
|
|
474
|
+
flag(:drafts, short: 'd', desc: 'filter: draft PRs')
|
|
475
|
+
flag(:pending, short: 'p', desc: 'filter: pending checks')
|
|
476
|
+
flag(:merged, short: 'm', desc: 'filter: merged PRs')
|
|
477
|
+
flag(:open, short: 'o', desc: 'filter: open (non-merged) PRs')
|
|
478
|
+
flag(:numbers, short: 'n', desc: 'output PR numbers only (no #)')
|
|
479
|
+
}
|
|
480
|
+
|
|
449
481
|
Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
450
482
|
pinned_manager = PinnedPRManager.new
|
|
451
483
|
|
|
@@ -692,47 +724,67 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
692
724
|
end
|
|
693
725
|
|
|
694
726
|
add_subcmd(:ls) do |*ls_args|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
727
|
+
opts = Hiiro::Options.parse(ls_args) {
|
|
728
|
+
flag(:update, short: 'u', desc: 'update status before listing')
|
|
729
|
+
instance_eval(&FILTER_OPTS)
|
|
730
|
+
}
|
|
698
731
|
|
|
699
|
-
if
|
|
700
|
-
puts
|
|
701
|
-
|
|
732
|
+
if opts.help
|
|
733
|
+
puts opts.help_text
|
|
734
|
+
exit 1
|
|
702
735
|
end
|
|
703
736
|
|
|
704
|
-
|
|
737
|
+
pinned = pinned_manager.load_pinned
|
|
738
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
739
|
+
|
|
740
|
+
if opts.update
|
|
705
741
|
puts "Updating status for #{pinned.length} PR(s)..."
|
|
706
742
|
pinned_manager.refresh_all_status(pinned, force: true)
|
|
707
743
|
pinned_manager.save_pinned(pinned)
|
|
708
744
|
puts
|
|
709
745
|
end
|
|
710
746
|
|
|
711
|
-
|
|
712
|
-
|
|
747
|
+
results = pinned_manager.apply_filters(pinned, opts)
|
|
748
|
+
|
|
749
|
+
if results.empty?
|
|
750
|
+
puts pinned_manager.filter_active?(opts) ? "No PRs match filters." : "No tracked PRs."
|
|
751
|
+
next
|
|
713
752
|
end
|
|
714
753
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
754
|
+
if opts.numbers
|
|
755
|
+
results.each { |pr| puts pr['number'] }
|
|
756
|
+
else
|
|
757
|
+
results.each_with_index { |pr, idx| puts pinned_manager.display_pinned(pr, idx) }
|
|
758
|
+
last_checked = pinned.filter_map { |pr| pr['last_checked'] }.max
|
|
759
|
+
puts "---"
|
|
760
|
+
puts "Last updated: #{last_checked || 'never'}"
|
|
761
|
+
end
|
|
718
762
|
end
|
|
719
763
|
|
|
720
764
|
add_subcmd(:status) do |*status_args|
|
|
765
|
+
opts = Hiiro::Options.parse(status_args, &FILTER_OPTS)
|
|
766
|
+
|
|
721
767
|
pinned = pinned_manager.load_pinned
|
|
768
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
722
769
|
|
|
723
|
-
|
|
724
|
-
|
|
770
|
+
results = pinned_manager.apply_filters(pinned, opts)
|
|
771
|
+
|
|
772
|
+
if results.empty?
|
|
773
|
+
puts pinned_manager.filter_active?(opts) ? "No PRs match filters." : "No tracked PRs."
|
|
725
774
|
next
|
|
726
775
|
end
|
|
727
776
|
|
|
728
|
-
|
|
729
|
-
puts
|
|
730
|
-
|
|
777
|
+
if opts.numbers
|
|
778
|
+
results.each { |pr| puts pr['number'] }
|
|
779
|
+
else
|
|
780
|
+
results.each_with_index do |pr, idx|
|
|
781
|
+
puts pinned_manager.display_detailed(pr, idx)
|
|
782
|
+
puts
|
|
783
|
+
end
|
|
784
|
+
last_checked = pinned.filter_map { |pr| pr['last_checked'] }.max
|
|
785
|
+
puts "---"
|
|
786
|
+
puts "Last updated: #{last_checked || 'never'}"
|
|
731
787
|
end
|
|
732
|
-
|
|
733
|
-
last_checked = pinned.filter_map { |pr| pr['last_checked'] }.max
|
|
734
|
-
puts "---"
|
|
735
|
-
puts "Last updated: #{last_checked || 'never'}"
|
|
736
788
|
end
|
|
737
789
|
|
|
738
790
|
add_subcmd(:update) do |*update_args|
|
|
@@ -756,54 +808,54 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
756
808
|
end
|
|
757
809
|
|
|
758
810
|
add_subcmd(:green) do |*green_args|
|
|
811
|
+
opts = Hiiro::Options.parse(green_args, &FILTER_OPTS)
|
|
759
812
|
pinned = pinned_manager.load_pinned
|
|
813
|
+
results = pinned_manager.apply_filters(pinned, opts, forced: [:green])
|
|
760
814
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
c && c['failed'] == 0 && c['pending'] == 0 && c['success'] > 0
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
if filtered.empty?
|
|
767
|
-
puts "No PRs with passing checks"
|
|
815
|
+
if results.empty?
|
|
816
|
+
puts "No PRs match."
|
|
768
817
|
next
|
|
769
818
|
end
|
|
770
819
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
820
|
+
if opts.numbers
|
|
821
|
+
results.each { |pr| puts pr['number'] }
|
|
822
|
+
else
|
|
823
|
+
results.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
|
|
824
|
+
end
|
|
774
825
|
end
|
|
775
826
|
|
|
776
827
|
add_subcmd(:red) do |*red_args|
|
|
828
|
+
opts = Hiiro::Options.parse(red_args, &FILTER_OPTS)
|
|
777
829
|
pinned = pinned_manager.load_pinned
|
|
830
|
+
results = pinned_manager.apply_filters(pinned, opts, forced: [:red])
|
|
778
831
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
c && c['failed'] > 0
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
if filtered.empty?
|
|
785
|
-
puts "No PRs with failing checks"
|
|
832
|
+
if results.empty?
|
|
833
|
+
puts "No PRs match."
|
|
786
834
|
next
|
|
787
835
|
end
|
|
788
836
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
837
|
+
if opts.numbers
|
|
838
|
+
results.each { |pr| puts pr['number'] }
|
|
839
|
+
else
|
|
840
|
+
results.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
|
|
841
|
+
end
|
|
792
842
|
end
|
|
793
843
|
|
|
794
844
|
add_subcmd(:old) do |*old_args|
|
|
845
|
+
opts = Hiiro::Options.parse(old_args, &FILTER_OPTS)
|
|
795
846
|
pinned = pinned_manager.load_pinned
|
|
847
|
+
results = pinned_manager.apply_filters(pinned, opts, forced: [:merged])
|
|
796
848
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
if filtered.empty?
|
|
800
|
-
puts "No merged PRs"
|
|
849
|
+
if results.empty?
|
|
850
|
+
puts "No PRs match."
|
|
801
851
|
next
|
|
802
852
|
end
|
|
803
853
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
854
|
+
if opts.numbers
|
|
855
|
+
results.each { |pr| puts pr['number'] }
|
|
856
|
+
else
|
|
857
|
+
results.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
|
|
858
|
+
end
|
|
807
859
|
end
|
|
808
860
|
|
|
809
861
|
add_subcmd(:prune) do |*args|
|
|
@@ -834,18 +886,20 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
834
886
|
end
|
|
835
887
|
|
|
836
888
|
add_subcmd(:draft) do |*draft_args|
|
|
889
|
+
opts = Hiiro::Options.parse(draft_args, &FILTER_OPTS)
|
|
837
890
|
pinned = pinned_manager.load_pinned
|
|
891
|
+
results = pinned_manager.apply_filters(pinned, opts, forced: [:drafts])
|
|
838
892
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
if filtered.empty?
|
|
842
|
-
puts "No draft PRs"
|
|
893
|
+
if results.empty?
|
|
894
|
+
puts "No PRs match."
|
|
843
895
|
next
|
|
844
896
|
end
|
|
845
897
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
898
|
+
if opts.numbers
|
|
899
|
+
results.each { |pr| puts pr['number'] }
|
|
900
|
+
else
|
|
901
|
+
results.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
|
|
902
|
+
end
|
|
849
903
|
end
|
|
850
904
|
|
|
851
905
|
add_subcmd(:assigned) do
|
|
@@ -1263,6 +1317,159 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
1263
1317
|
matches.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
|
|
1264
1318
|
end
|
|
1265
1319
|
|
|
1320
|
+
# === Multi-PR action commands (mCMD) ===
|
|
1321
|
+
# Each accepts composable filter flags and applies the action to all matching PRs.
|
|
1322
|
+
# Without filters, falls back to fuzzy-select.
|
|
1323
|
+
|
|
1324
|
+
resolve_multi = ->(pinned, opts) {
|
|
1325
|
+
if pinned_manager.filter_active?(opts)
|
|
1326
|
+
results = pinned_manager.apply_filters(pinned, opts)
|
|
1327
|
+
if results.empty?
|
|
1328
|
+
puts "No PRs match filters."
|
|
1329
|
+
return nil
|
|
1330
|
+
end
|
|
1331
|
+
results
|
|
1332
|
+
else
|
|
1333
|
+
lines = pinned.each_with_index.each_with_object({}) { |(pr, i), h| h[pinned_manager.display_pinned(pr, i)] = pr }
|
|
1334
|
+
sel = fuzzyfind_from_map(lines)
|
|
1335
|
+
sel ? [sel] : nil
|
|
1336
|
+
end
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
add_subcmd(:mfix) do |*args|
|
|
1340
|
+
opts = Hiiro::Options.parse(args) {
|
|
1341
|
+
instance_eval(&FILTER_OPTS)
|
|
1342
|
+
flag(:run, short: 'R', desc: 'launch tasks immediately after queuing')
|
|
1343
|
+
}
|
|
1344
|
+
pinned = pinned_manager.load_pinned
|
|
1345
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1346
|
+
|
|
1347
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1348
|
+
next unless prs
|
|
1349
|
+
|
|
1350
|
+
q = Hiiro::Queue.current(self)
|
|
1351
|
+
queued_names = []
|
|
1352
|
+
|
|
1353
|
+
prs.each do |pr|
|
|
1354
|
+
task_info = { task_name: pr['task'], tree_name: pr['worktree'], session_name: pr['tmux_session'] }.compact
|
|
1355
|
+
task_info = nil if task_info.empty?
|
|
1356
|
+
result = q.add_with_frontmatter("/pr:fix #{pr['number']}", task_info: task_info)
|
|
1357
|
+
if result
|
|
1358
|
+
puts "Queued fix for ##{pr['number']}: #{pr['title']}"
|
|
1359
|
+
queued_names << result[:name]
|
|
1360
|
+
else
|
|
1361
|
+
STDERR.puts "Failed to queue fix for ##{pr['number']}"
|
|
1362
|
+
end
|
|
1363
|
+
end
|
|
1364
|
+
|
|
1365
|
+
if queued_names.any?
|
|
1366
|
+
puts
|
|
1367
|
+
puts "Queued #{queued_names.length} fix task(s)."
|
|
1368
|
+
if opts.run
|
|
1369
|
+
puts "Launching..."
|
|
1370
|
+
queued_names.each { |name| q.launch_task(name) }
|
|
1371
|
+
else
|
|
1372
|
+
puts "Run 'h queue run' to start."
|
|
1373
|
+
end
|
|
1374
|
+
end
|
|
1375
|
+
end
|
|
1376
|
+
|
|
1377
|
+
add_subcmd(:mopen) do |*args|
|
|
1378
|
+
opts = Hiiro::Options.parse(args, &FILTER_OPTS)
|
|
1379
|
+
pinned = pinned_manager.load_pinned
|
|
1380
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1381
|
+
|
|
1382
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1383
|
+
next unless prs
|
|
1384
|
+
|
|
1385
|
+
prs.each { |pr| system('gh', 'pr', 'view', pr['number'].to_s, '--web') }
|
|
1386
|
+
end
|
|
1387
|
+
|
|
1388
|
+
add_subcmd(:mmerge) do |*args|
|
|
1389
|
+
opts = Hiiro::Options.parse(args) {
|
|
1390
|
+
instance_eval(&FILTER_OPTS)
|
|
1391
|
+
option(:strategy, short: 's', desc: 'merge strategy (merge, squash, rebase)')
|
|
1392
|
+
}
|
|
1393
|
+
pinned = pinned_manager.load_pinned
|
|
1394
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1395
|
+
|
|
1396
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1397
|
+
next unless prs
|
|
1398
|
+
|
|
1399
|
+
merge_args = opts.strategy ? ["--#{opts.strategy}"] : []
|
|
1400
|
+
prs.each do |pr|
|
|
1401
|
+
puts "Merging ##{pr['number']}: #{pr['title']}"
|
|
1402
|
+
system('gh', 'pr', 'merge', pr['number'].to_s, *merge_args)
|
|
1403
|
+
end
|
|
1404
|
+
end
|
|
1405
|
+
|
|
1406
|
+
add_subcmd(:mready) do |*args|
|
|
1407
|
+
opts = Hiiro::Options.parse(args, &FILTER_OPTS)
|
|
1408
|
+
pinned = pinned_manager.load_pinned
|
|
1409
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1410
|
+
|
|
1411
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1412
|
+
next unless prs
|
|
1413
|
+
|
|
1414
|
+
prs.each do |pr|
|
|
1415
|
+
puts "Marking ##{pr['number']} as ready: #{pr['title']}"
|
|
1416
|
+
system('gh', 'pr', 'ready', pr['number'].to_s)
|
|
1417
|
+
end
|
|
1418
|
+
end
|
|
1419
|
+
|
|
1420
|
+
add_subcmd(:'mto-draft') do |*args|
|
|
1421
|
+
opts = Hiiro::Options.parse(args, &FILTER_OPTS)
|
|
1422
|
+
pinned = pinned_manager.load_pinned
|
|
1423
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1424
|
+
|
|
1425
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1426
|
+
next unless prs
|
|
1427
|
+
|
|
1428
|
+
prs.each do |pr|
|
|
1429
|
+
puts "Converting ##{pr['number']} to draft: #{pr['title']}"
|
|
1430
|
+
system('gh', 'pr', 'ready', '--draft', pr['number'].to_s)
|
|
1431
|
+
end
|
|
1432
|
+
end
|
|
1433
|
+
|
|
1434
|
+
add_subcmd(:mrm) do |*args|
|
|
1435
|
+
opts = Hiiro::Options.parse(args, &FILTER_OPTS)
|
|
1436
|
+
pinned = pinned_manager.load_pinned
|
|
1437
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1438
|
+
|
|
1439
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1440
|
+
next unless prs
|
|
1441
|
+
|
|
1442
|
+
prs.each do |pr|
|
|
1443
|
+
pinned_manager.unpin(pr['number'])
|
|
1444
|
+
puts "Removed ##{pr['number']}: #{pr['title']}"
|
|
1445
|
+
end
|
|
1446
|
+
end
|
|
1447
|
+
|
|
1448
|
+
add_subcmd(:mcomment) do |*args|
|
|
1449
|
+
opts = Hiiro::Options.parse(args, &FILTER_OPTS)
|
|
1450
|
+
pinned = pinned_manager.load_pinned
|
|
1451
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1452
|
+
|
|
1453
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1454
|
+
next unless prs
|
|
1455
|
+
|
|
1456
|
+
tmpfile = Tempfile.new(['pr-comment-', '.md'])
|
|
1457
|
+
tmpfile.close
|
|
1458
|
+
edit_files(tmpfile.path)
|
|
1459
|
+
body = File.read(tmpfile.path).strip
|
|
1460
|
+
tmpfile.unlink
|
|
1461
|
+
|
|
1462
|
+
if body.empty?
|
|
1463
|
+
puts "Aborted: empty comment"
|
|
1464
|
+
next
|
|
1465
|
+
end
|
|
1466
|
+
|
|
1467
|
+
prs.each do |pr|
|
|
1468
|
+
puts "Commenting on ##{pr['number']}: #{pr['title']}"
|
|
1469
|
+
system('gh', 'pr', 'comment', pr['number'].to_s, '--body', body)
|
|
1470
|
+
end
|
|
1471
|
+
end
|
|
1472
|
+
|
|
1266
1473
|
add_subcmd(:config) do |*args|
|
|
1267
1474
|
make_child do
|
|
1268
1475
|
add_subcmd(:path) do
|
data/lib/hiiro/version.rb
CHANGED