hiiro 0.1.233 → 0.1.234
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 +258 -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: 8460afefae6d7bd3f6ba3d2fb8f71a11b6c78afab3056bc103e3ed3c7a20eba1
|
|
4
|
+
data.tar.gz: ec62d0166213c073e62040fa456732e1b75cd3060a0d76ee2061bd921fa69047
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 211bafbcf686e09654a3b983cb99fb8327f43273b89bb0e9a9c1c52971572f17eb8fafc4b95bcb96b3ff182b83e65218a3aa62df05b2ef75e2b3be390c1adfc8
|
|
7
|
+
data.tar.gz: feaa58f7885e1b8ecc0fc3f06f4b8e40b4f1f1b319ae37110772544f96abfb6ba1197d18966f6af4540dafadf3122fdcea2abcb0409470e514291ec1c23e49f7
|
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
|
+
open: ->(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,62 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
692
724
|
end
|
|
693
725
|
|
|
694
726
|
add_subcmd(:ls) do |*ls_args|
|
|
695
|
-
|
|
727
|
+
opts = Hiiro::Options.parse(ls_args) {
|
|
728
|
+
flag(:update, short: 'u', desc: 'update status before listing')
|
|
729
|
+
instance_eval(&FILTER_OPTS)
|
|
730
|
+
}
|
|
696
731
|
|
|
697
732
|
pinned = pinned_manager.load_pinned
|
|
733
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
698
734
|
|
|
699
|
-
if
|
|
700
|
-
puts "No tracked PRs"
|
|
701
|
-
next
|
|
702
|
-
end
|
|
703
|
-
|
|
704
|
-
if update
|
|
735
|
+
if opts.update
|
|
705
736
|
puts "Updating status for #{pinned.length} PR(s)..."
|
|
706
737
|
pinned_manager.refresh_all_status(pinned, force: true)
|
|
707
738
|
pinned_manager.save_pinned(pinned)
|
|
708
739
|
puts
|
|
709
740
|
end
|
|
710
741
|
|
|
711
|
-
|
|
712
|
-
|
|
742
|
+
results = pinned_manager.apply_filters(pinned, opts)
|
|
743
|
+
|
|
744
|
+
if results.empty?
|
|
745
|
+
puts pinned_manager.filter_active?(opts) ? "No PRs match filters." : "No tracked PRs."
|
|
746
|
+
next
|
|
713
747
|
end
|
|
714
748
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
749
|
+
if opts.numbers
|
|
750
|
+
results.each { |pr| puts pr['number'] }
|
|
751
|
+
else
|
|
752
|
+
results.each_with_index { |pr, idx| puts pinned_manager.display_pinned(pr, idx) }
|
|
753
|
+
last_checked = pinned.filter_map { |pr| pr['last_checked'] }.max
|
|
754
|
+
puts "---"
|
|
755
|
+
puts "Last updated: #{last_checked || 'never'}"
|
|
756
|
+
end
|
|
718
757
|
end
|
|
719
758
|
|
|
720
759
|
add_subcmd(:status) do |*status_args|
|
|
760
|
+
opts = Hiiro::Options.parse(status_args, &FILTER_OPTS)
|
|
761
|
+
|
|
721
762
|
pinned = pinned_manager.load_pinned
|
|
763
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
722
764
|
|
|
723
|
-
|
|
724
|
-
|
|
765
|
+
results = pinned_manager.apply_filters(pinned, opts)
|
|
766
|
+
|
|
767
|
+
if results.empty?
|
|
768
|
+
puts pinned_manager.filter_active?(opts) ? "No PRs match filters." : "No tracked PRs."
|
|
725
769
|
next
|
|
726
770
|
end
|
|
727
771
|
|
|
728
|
-
|
|
729
|
-
puts
|
|
730
|
-
|
|
772
|
+
if opts.numbers
|
|
773
|
+
results.each { |pr| puts pr['number'] }
|
|
774
|
+
else
|
|
775
|
+
results.each_with_index do |pr, idx|
|
|
776
|
+
puts pinned_manager.display_detailed(pr, idx)
|
|
777
|
+
puts
|
|
778
|
+
end
|
|
779
|
+
last_checked = pinned.filter_map { |pr| pr['last_checked'] }.max
|
|
780
|
+
puts "---"
|
|
781
|
+
puts "Last updated: #{last_checked || 'never'}"
|
|
731
782
|
end
|
|
732
|
-
|
|
733
|
-
last_checked = pinned.filter_map { |pr| pr['last_checked'] }.max
|
|
734
|
-
puts "---"
|
|
735
|
-
puts "Last updated: #{last_checked || 'never'}"
|
|
736
783
|
end
|
|
737
784
|
|
|
738
785
|
add_subcmd(:update) do |*update_args|
|
|
@@ -756,54 +803,54 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
756
803
|
end
|
|
757
804
|
|
|
758
805
|
add_subcmd(:green) do |*green_args|
|
|
806
|
+
opts = Hiiro::Options.parse(green_args, &FILTER_OPTS)
|
|
759
807
|
pinned = pinned_manager.load_pinned
|
|
808
|
+
results = pinned_manager.apply_filters(pinned, opts, forced: [:green])
|
|
760
809
|
|
|
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"
|
|
810
|
+
if results.empty?
|
|
811
|
+
puts "No PRs match."
|
|
768
812
|
next
|
|
769
813
|
end
|
|
770
814
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
815
|
+
if opts.numbers
|
|
816
|
+
results.each { |pr| puts pr['number'] }
|
|
817
|
+
else
|
|
818
|
+
results.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
|
|
819
|
+
end
|
|
774
820
|
end
|
|
775
821
|
|
|
776
822
|
add_subcmd(:red) do |*red_args|
|
|
823
|
+
opts = Hiiro::Options.parse(red_args, &FILTER_OPTS)
|
|
777
824
|
pinned = pinned_manager.load_pinned
|
|
825
|
+
results = pinned_manager.apply_filters(pinned, opts, forced: [:red])
|
|
778
826
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
c && c['failed'] > 0
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
if filtered.empty?
|
|
785
|
-
puts "No PRs with failing checks"
|
|
827
|
+
if results.empty?
|
|
828
|
+
puts "No PRs match."
|
|
786
829
|
next
|
|
787
830
|
end
|
|
788
831
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
832
|
+
if opts.numbers
|
|
833
|
+
results.each { |pr| puts pr['number'] }
|
|
834
|
+
else
|
|
835
|
+
results.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
|
|
836
|
+
end
|
|
792
837
|
end
|
|
793
838
|
|
|
794
839
|
add_subcmd(:old) do |*old_args|
|
|
840
|
+
opts = Hiiro::Options.parse(old_args, &FILTER_OPTS)
|
|
795
841
|
pinned = pinned_manager.load_pinned
|
|
842
|
+
results = pinned_manager.apply_filters(pinned, opts, forced: [:merged])
|
|
796
843
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
if filtered.empty?
|
|
800
|
-
puts "No merged PRs"
|
|
844
|
+
if results.empty?
|
|
845
|
+
puts "No PRs match."
|
|
801
846
|
next
|
|
802
847
|
end
|
|
803
848
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
849
|
+
if opts.numbers
|
|
850
|
+
results.each { |pr| puts pr['number'] }
|
|
851
|
+
else
|
|
852
|
+
results.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
|
|
853
|
+
end
|
|
807
854
|
end
|
|
808
855
|
|
|
809
856
|
add_subcmd(:prune) do |*args|
|
|
@@ -834,18 +881,20 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
834
881
|
end
|
|
835
882
|
|
|
836
883
|
add_subcmd(:draft) do |*draft_args|
|
|
884
|
+
opts = Hiiro::Options.parse(draft_args, &FILTER_OPTS)
|
|
837
885
|
pinned = pinned_manager.load_pinned
|
|
886
|
+
results = pinned_manager.apply_filters(pinned, opts, forced: [:drafts])
|
|
838
887
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
if filtered.empty?
|
|
842
|
-
puts "No draft PRs"
|
|
888
|
+
if results.empty?
|
|
889
|
+
puts "No PRs match."
|
|
843
890
|
next
|
|
844
891
|
end
|
|
845
892
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
893
|
+
if opts.numbers
|
|
894
|
+
results.each { |pr| puts pr['number'] }
|
|
895
|
+
else
|
|
896
|
+
results.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
|
|
897
|
+
end
|
|
849
898
|
end
|
|
850
899
|
|
|
851
900
|
add_subcmd(:assigned) do
|
|
@@ -1263,6 +1312,159 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
1263
1312
|
matches.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
|
|
1264
1313
|
end
|
|
1265
1314
|
|
|
1315
|
+
# === Multi-PR action commands (mCMD) ===
|
|
1316
|
+
# Each accepts composable filter flags and applies the action to all matching PRs.
|
|
1317
|
+
# Without filters, falls back to fuzzy-select.
|
|
1318
|
+
|
|
1319
|
+
resolve_multi = ->(pinned, opts) {
|
|
1320
|
+
if pinned_manager.filter_active?(opts)
|
|
1321
|
+
results = pinned_manager.apply_filters(pinned, opts)
|
|
1322
|
+
if results.empty?
|
|
1323
|
+
puts "No PRs match filters."
|
|
1324
|
+
return nil
|
|
1325
|
+
end
|
|
1326
|
+
results
|
|
1327
|
+
else
|
|
1328
|
+
lines = pinned.each_with_index.each_with_object({}) { |(pr, i), h| h[pinned_manager.display_pinned(pr, i)] = pr }
|
|
1329
|
+
sel = fuzzyfind_from_map(lines)
|
|
1330
|
+
sel ? [sel] : nil
|
|
1331
|
+
end
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
add_subcmd(:mfix) do |*args|
|
|
1335
|
+
opts = Hiiro::Options.parse(args) {
|
|
1336
|
+
instance_eval(&FILTER_OPTS)
|
|
1337
|
+
flag(:run, short: 'R', desc: 'launch tasks immediately after queuing')
|
|
1338
|
+
}
|
|
1339
|
+
pinned = pinned_manager.load_pinned
|
|
1340
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1341
|
+
|
|
1342
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1343
|
+
next unless prs
|
|
1344
|
+
|
|
1345
|
+
q = Hiiro::Queue.current(self)
|
|
1346
|
+
queued_names = []
|
|
1347
|
+
|
|
1348
|
+
prs.each do |pr|
|
|
1349
|
+
task_info = { task_name: pr['task'], tree_name: pr['worktree'], session_name: pr['tmux_session'] }.compact
|
|
1350
|
+
task_info = nil if task_info.empty?
|
|
1351
|
+
result = q.add_with_frontmatter("/pr:fix #{pr['number']}", task_info: task_info)
|
|
1352
|
+
if result
|
|
1353
|
+
puts "Queued fix for ##{pr['number']}: #{pr['title']}"
|
|
1354
|
+
queued_names << result[:name]
|
|
1355
|
+
else
|
|
1356
|
+
STDERR.puts "Failed to queue fix for ##{pr['number']}"
|
|
1357
|
+
end
|
|
1358
|
+
end
|
|
1359
|
+
|
|
1360
|
+
if queued_names.any?
|
|
1361
|
+
puts
|
|
1362
|
+
puts "Queued #{queued_names.length} fix task(s)."
|
|
1363
|
+
if opts.run
|
|
1364
|
+
puts "Launching..."
|
|
1365
|
+
queued_names.each { |name| q.launch_task(name) }
|
|
1366
|
+
else
|
|
1367
|
+
puts "Run 'h queue run' to start."
|
|
1368
|
+
end
|
|
1369
|
+
end
|
|
1370
|
+
end
|
|
1371
|
+
|
|
1372
|
+
add_subcmd(:mopen) do |*args|
|
|
1373
|
+
opts = Hiiro::Options.parse(args, &FILTER_OPTS)
|
|
1374
|
+
pinned = pinned_manager.load_pinned
|
|
1375
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1376
|
+
|
|
1377
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1378
|
+
next unless prs
|
|
1379
|
+
|
|
1380
|
+
prs.each { |pr| system('gh', 'pr', 'view', pr['number'].to_s, '--web') }
|
|
1381
|
+
end
|
|
1382
|
+
|
|
1383
|
+
add_subcmd(:mmerge) do |*args|
|
|
1384
|
+
opts = Hiiro::Options.parse(args) {
|
|
1385
|
+
instance_eval(&FILTER_OPTS)
|
|
1386
|
+
option(:strategy, short: 's', desc: 'merge strategy (merge, squash, rebase)')
|
|
1387
|
+
}
|
|
1388
|
+
pinned = pinned_manager.load_pinned
|
|
1389
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1390
|
+
|
|
1391
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1392
|
+
next unless prs
|
|
1393
|
+
|
|
1394
|
+
merge_args = opts.strategy ? ["--#{opts.strategy}"] : []
|
|
1395
|
+
prs.each do |pr|
|
|
1396
|
+
puts "Merging ##{pr['number']}: #{pr['title']}"
|
|
1397
|
+
system('gh', 'pr', 'merge', pr['number'].to_s, *merge_args)
|
|
1398
|
+
end
|
|
1399
|
+
end
|
|
1400
|
+
|
|
1401
|
+
add_subcmd(:mready) do |*args|
|
|
1402
|
+
opts = Hiiro::Options.parse(args, &FILTER_OPTS)
|
|
1403
|
+
pinned = pinned_manager.load_pinned
|
|
1404
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1405
|
+
|
|
1406
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1407
|
+
next unless prs
|
|
1408
|
+
|
|
1409
|
+
prs.each do |pr|
|
|
1410
|
+
puts "Marking ##{pr['number']} as ready: #{pr['title']}"
|
|
1411
|
+
system('gh', 'pr', 'ready', pr['number'].to_s)
|
|
1412
|
+
end
|
|
1413
|
+
end
|
|
1414
|
+
|
|
1415
|
+
add_subcmd(:'mto-draft') do |*args|
|
|
1416
|
+
opts = Hiiro::Options.parse(args, &FILTER_OPTS)
|
|
1417
|
+
pinned = pinned_manager.load_pinned
|
|
1418
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1419
|
+
|
|
1420
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1421
|
+
next unless prs
|
|
1422
|
+
|
|
1423
|
+
prs.each do |pr|
|
|
1424
|
+
puts "Converting ##{pr['number']} to draft: #{pr['title']}"
|
|
1425
|
+
system('gh', 'pr', 'ready', '--draft', pr['number'].to_s)
|
|
1426
|
+
end
|
|
1427
|
+
end
|
|
1428
|
+
|
|
1429
|
+
add_subcmd(:mrm) do |*args|
|
|
1430
|
+
opts = Hiiro::Options.parse(args, &FILTER_OPTS)
|
|
1431
|
+
pinned = pinned_manager.load_pinned
|
|
1432
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1433
|
+
|
|
1434
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1435
|
+
next unless prs
|
|
1436
|
+
|
|
1437
|
+
prs.each do |pr|
|
|
1438
|
+
pinned_manager.unpin(pr['number'])
|
|
1439
|
+
puts "Removed ##{pr['number']}: #{pr['title']}"
|
|
1440
|
+
end
|
|
1441
|
+
end
|
|
1442
|
+
|
|
1443
|
+
add_subcmd(:mcomment) do |*args|
|
|
1444
|
+
opts = Hiiro::Options.parse(args, &FILTER_OPTS)
|
|
1445
|
+
pinned = pinned_manager.load_pinned
|
|
1446
|
+
next puts "No tracked PRs" if pinned.empty?
|
|
1447
|
+
|
|
1448
|
+
prs = resolve_multi.call(pinned, opts)
|
|
1449
|
+
next unless prs
|
|
1450
|
+
|
|
1451
|
+
tmpfile = Tempfile.new(['pr-comment-', '.md'])
|
|
1452
|
+
tmpfile.close
|
|
1453
|
+
edit_files(tmpfile.path)
|
|
1454
|
+
body = File.read(tmpfile.path).strip
|
|
1455
|
+
tmpfile.unlink
|
|
1456
|
+
|
|
1457
|
+
if body.empty?
|
|
1458
|
+
puts "Aborted: empty comment"
|
|
1459
|
+
next
|
|
1460
|
+
end
|
|
1461
|
+
|
|
1462
|
+
prs.each do |pr|
|
|
1463
|
+
puts "Commenting on ##{pr['number']}: #{pr['title']}"
|
|
1464
|
+
system('gh', 'pr', 'comment', pr['number'].to_s, '--body', body)
|
|
1465
|
+
end
|
|
1466
|
+
end
|
|
1467
|
+
|
|
1266
1468
|
add_subcmd(:config) do |*args|
|
|
1267
1469
|
make_child do
|
|
1268
1470
|
add_subcmd(:path) do
|
data/lib/hiiro/version.rb
CHANGED