66
77require "abstract_command"
88require "executables_db"
9+ require "utils/github"
910
1011module Homebrew
1112 module DevCmd
@@ -19,20 +20,23 @@ class WhichUpdate < AbstractCommand
1920 "list of missing formulae)."
2021 switch "--commit" ,
2122 description : "Commit the changes using `git`."
22- switch "--update-existing" ,
23- description : "Update database entries with outdated formula versions."
24- switch "--install-missing" ,
25- description : "Install and update formulae that are missing from the database and don't have bottles."
26- switch "--eval-all" ,
27- description : "Evaluate all installed taps, rather than just the core tap."
28- flag "--max-downloads=" ,
29- description : "Specify a maximum number of formulae to download and update."
23+ flag "--bottle-json-dir=" ,
24+ description : "Use generated bottle JSON files in the given directory to update formula entries."
25+ flag "--formulae-file=" ,
26+ description : "Update database entries for formulae listed in the given file."
27+ flag "--removed-formulae-file=" ,
28+ description : "Remove database entries for formulae listed in the given file."
29+ flag "--pull-request=" ,
30+ description : "Update entries for formula changes in the given pull request number."
31+ flag "--repository=" ,
32+ description : "GitHub repository for `--pull-request` (default: `$GITHUB_REPOSITORY`)."
3033 flag "--summary-file=" ,
3134 description : "Output a summary of the changes to a file."
3235 conflicts "--stats" , "--commit"
33- conflicts "--stats" , "--install-missing"
34- conflicts "--stats" , "--update-existing"
35- conflicts "--stats" , "--max-downloads"
36+ conflicts "--stats" , "--bottle-json-dir"
37+ conflicts "--stats" , "--formulae-file"
38+ conflicts "--stats" , "--removed-formulae-file"
39+ conflicts "--stats" , "--pull-request"
3640 named_args :database , number : 1
3741 end
3842
@@ -41,13 +45,18 @@ def run
4145 if args . stats?
4246 stats source : args . named . fetch ( 0 )
4347 else
44- update_and_save! source : args . named . fetch ( 0 ) ,
45- commit : args . commit? ,
46- update_existing : args . update_existing? ,
47- install_missing : args . install_missing? ,
48- max_downloads : args . max_downloads &.to_i ,
49- eval_all : args . eval_all? ,
50- summary_file : args . summary_file
48+ updated = update_and_save! source : args . named . fetch ( 0 ) ,
49+ commit : args . commit? ,
50+ bottle_json_dir : args . bottle_json_dir ,
51+ formulae_file : args . formulae_file ,
52+ removed_formulae_file : args . removed_formulae_file ,
53+ pull_request : args . pull_request ,
54+ repository : args . repository ,
55+ summary_file : args . summary_file
56+
57+ if ( github_output = ENV [ "GITHUB_OUTPUT" ] . presence )
58+ File . open ( github_output , "a" ) { |file | file . puts "updated=#{ updated } " }
59+ end
5160 end
5261 end
5362
@@ -77,20 +86,54 @@ def stats(source:)
7786
7887 sig {
7988 params (
80- source : String ,
81- commit : T ::Boolean ,
82- update_existing : T ::Boolean ,
83- install_missing : T ::Boolean ,
84- max_downloads : T . nilable ( Integer ) ,
85- eval_all : T ::Boolean ,
86- summary_file : T . nilable ( String ) ,
87- ) . void
89+ source : String ,
90+ commit : T ::Boolean ,
91+ bottle_json_dir : T . nilable ( String ) ,
92+ formulae_file : T . nilable ( String ) ,
93+ removed_formulae_file : T . nilable ( String ) ,
94+ pull_request : T . nilable ( String ) ,
95+ repository : T . nilable ( String ) ,
96+ summary_file : T . nilable ( String ) ,
97+ ) . returns ( T ::Boolean )
8898 }
89- def update_and_save! ( source :, commit : false , update_existing : false , install_missing : false ,
90- max_downloads : nil , eval_all : false , summary_file : nil )
99+ def update_and_save! ( source :, commit : false , bottle_json_dir : nil , formulae_file : nil ,
100+ removed_formulae_file : nil , pull_request : nil , repository : nil , summary_file : nil )
101+ source_path = Pathname ( source )
102+ original_database = source_path . exist? ? source_path . read : nil
91103 db = ExecutablesDB . new source
92- db . update! ( update_existing :, install_missing :,
93- max_downloads :, eval_all :)
104+
105+ formulae = file_lines ( formulae_file ) . map { |name | File . basename ( name , ".rb" ) }
106+ removed_formulae = file_lines ( removed_formulae_file ) . map { |name | File . basename ( name , ".rb" ) }
107+
108+ if pull_request
109+ repository = repository . presence || ENV [ "GITHUB_REPOSITORY" ] . presence
110+ if repository . blank?
111+ raise UsageError ,
112+ "`--repository` or `$GITHUB_REPOSITORY` is required with `--pull-request`."
113+ end
114+
115+ owner , repo = repository . split ( "/" , 2 )
116+ raise UsageError , "`--repository` must be in the form `owner/repo`." if owner . blank? || repo . blank?
117+
118+ GitHub ::API . paginate_rest ( GitHub . url_to ( "repos" , owner , repo , "pulls" , pull_request , "files" ) ) do |files |
119+ T . cast ( files , T ::Array [ T ::Hash [ String , T . untyped ] ] ) . each do |file |
120+ filename = file [ "filename" ] . to_s
121+ next if !filename . start_with? ( "Formula/" ) || !filename . end_with? ( ".rb" )
122+
123+ case file [ "status" ] . to_s
124+ when "removed"
125+ removed_formulae << File . basename ( filename , ".rb" )
126+ when "renamed"
127+ removed_formulae << File . basename ( file [ "previous_filename" ] . to_s , ".rb" )
128+ formulae << File . basename ( filename , ".rb" )
129+ else
130+ formulae << File . basename ( filename , ".rb" )
131+ end
132+ end
133+ end
134+ end
135+
136+ db . update! ( bottle_json_dir :, formulae :, removed_formulae :)
94137 db . save!
95138
96139 if summary_file
@@ -100,10 +143,11 @@ def update_and_save!(source:, commit: false, update_existing: false, install_mis
100143 end
101144 end
102145
103- return if !commit || !db . changed?
146+ return original_database != source_path . read if !commit || !db . changed?
104147
105148 msg = git_commit_message ( db . changes )
106149 safe_system "git" , "-C" , db . root . to_s , "commit" , "-m" , msg , source
150+ original_database != source_path . read
107151 end
108152
109153 sig { params ( els : T ::Array [ String ] , verb : String ) . returns ( String ) }
@@ -121,7 +165,6 @@ def git_commit_message(changes)
121165 names = changes . send ( action )
122166 next if names . empty?
123167
124- action = "bump version for" if action == :version_bump
125168 msg << english_list ( names . to_a . sort , action . to_s )
126169 break
127170 end
@@ -152,6 +195,17 @@ def summary_file_message(changes)
152195 #{ msg . join ( "\n " ) }
153196 MESSAGE
154197 end
198+
199+ private
200+
201+ sig { params ( path : T . nilable ( String ) ) . returns ( T ::Array [ String ] ) }
202+ def file_lines ( path )
203+ return [ ] if path . blank? || !File . file? ( path )
204+
205+ File . readlines ( path , chomp : true ) . filter_map do |line |
206+ line . strip . presence
207+ end
208+ end
155209 end
156210 end
157211end
0 commit comments