66
77require "abstract_command"
88require "executables_db"
9+ require "utils/github"
910
1011module Homebrew
1112 module DevCmd
@@ -19,20 +20,24 @@ 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+ depends_on : "--pull-request" ,
33+ description : "GitHub repository for `--pull-request` (default: `$GITHUB_REPOSITORY`)."
3034 flag "--summary-file=" ,
3135 description : "Output a summary of the changes to a file."
3236 conflicts "--stats" , "--commit"
33- conflicts "--stats" , "--install-missing"
34- conflicts "--stats" , "--update-existing"
35- conflicts "--stats" , "--max-downloads"
37+ conflicts "--stats" , "--bottle-json-dir"
38+ conflicts "--stats" , "--formulae-file"
39+ conflicts "--stats" , "--removed-formulae-file"
40+ conflicts "--stats" , "--pull-request"
3641 named_args :database , number : 1
3742 end
3843
@@ -41,13 +46,18 @@ def run
4146 if args . stats?
4247 stats source : args . named . fetch ( 0 )
4348 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
49+ updated = update_and_save! source : args . named . fetch ( 0 ) ,
50+ commit : args . commit? ,
51+ bottle_json_dir : args . bottle_json_dir ,
52+ formulae_file : args . formulae_file ,
53+ removed_formulae_file : args . removed_formulae_file ,
54+ pull_request : args . pull_request ,
55+ repository : args . repository ,
56+ summary_file : args . summary_file
57+
58+ if ( github_output = ENV [ "GITHUB_OUTPUT" ] . presence )
59+ File . open ( github_output , "a" ) { |file | file . puts "updated=#{ updated } " }
60+ end
5161 end
5262 end
5363
@@ -77,20 +87,54 @@ def stats(source:)
7787
7888 sig {
7989 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
90+ source : String ,
91+ commit : T ::Boolean ,
92+ bottle_json_dir : T . nilable ( String ) ,
93+ formulae_file : T . nilable ( String ) ,
94+ removed_formulae_file : T . nilable ( String ) ,
95+ pull_request : T . nilable ( String ) ,
96+ repository : T . nilable ( String ) ,
97+ summary_file : T . nilable ( String ) ,
98+ ) . returns ( T ::Boolean )
8899 }
89- def update_and_save! ( source :, commit : false , update_existing : false , install_missing : false ,
90- max_downloads : nil , eval_all : false , summary_file : nil )
100+ def update_and_save! ( source :, commit : false , bottle_json_dir : nil , formulae_file : nil ,
101+ removed_formulae_file : nil , pull_request : nil , repository : nil , summary_file : nil )
102+ source_path = Pathname ( source )
103+ original_database = source_path . exist? ? source_path . read : nil
91104 db = ExecutablesDB . new source
92- db . update! ( update_existing :, install_missing :,
93- max_downloads :, eval_all :)
105+
106+ formulae = file_lines ( formulae_file )
107+ removed_formulae = file_lines ( removed_formulae_file )
108+
109+ if pull_request
110+ repository = repository . presence || ENV [ "GITHUB_REPOSITORY" ] . presence
111+ if repository . blank?
112+ raise UsageError ,
113+ "`--repository` or `$GITHUB_REPOSITORY` is required with `--pull-request`."
114+ end
115+
116+ owner , repo = repository . split ( "/" , 2 )
117+ raise UsageError , "`--repository` must be in the form `owner/repo`." if owner . blank? || repo . blank?
118+
119+ GitHub ::API . paginate_rest ( GitHub . url_to ( "repos" , owner , repo , "pulls" , pull_request , "files" ) ) do |files |
120+ T . cast ( files , T ::Array [ T ::Hash [ String , T . untyped ] ] ) . each do |file |
121+ filename = file [ "filename" ] . to_s
122+ next if !filename . start_with? ( "Formula/" ) || !filename . end_with? ( ".rb" )
123+
124+ case file [ "status" ] . to_s
125+ when "removed"
126+ removed_formulae << File . basename ( filename , ".rb" )
127+ when "renamed"
128+ removed_formulae << File . basename ( file [ "previous_filename" ] . to_s , ".rb" )
129+ formulae << File . basename ( filename , ".rb" )
130+ else
131+ formulae << File . basename ( filename , ".rb" )
132+ end
133+ end
134+ end
135+ end
136+
137+ db . update! ( bottle_json_dir :, formulae :, removed_formulae :)
94138 db . save!
95139
96140 if summary_file
@@ -100,10 +144,11 @@ def update_and_save!(source:, commit: false, update_existing: false, install_mis
100144 end
101145 end
102146
103- return if !commit || !db . changed?
147+ return original_database != source_path . read if !commit || !db . changed?
104148
105149 msg = git_commit_message ( db . changes )
106150 safe_system "git" , "-C" , db . root . to_s , "commit" , "-m" , msg , source
151+ original_database != source_path . read
107152 end
108153
109154 sig { params ( els : T ::Array [ String ] , verb : String ) . returns ( String ) }
@@ -121,7 +166,6 @@ def git_commit_message(changes)
121166 names = changes . send ( action )
122167 next if names . empty?
123168
124- action = "bump version for" if action == :version_bump
125169 msg << english_list ( names . to_a . sort , action . to_s )
126170 break
127171 end
@@ -152,6 +196,17 @@ def summary_file_message(changes)
152196 #{ msg . join ( "\n " ) }
153197 MESSAGE
154198 end
199+
200+ private
201+
202+ sig { params ( path : T . nilable ( String ) ) . returns ( T ::Array [ String ] ) }
203+ def file_lines ( path )
204+ return [ ] if path . blank? || !File . file? ( path )
205+
206+ File . readlines ( path , chomp : true ) . filter_map do |line |
207+ line . strip . presence
208+ end
209+ end
155210 end
156211 end
157212end
0 commit comments