@@ -536,6 +536,40 @@ def print_repo_hashes(args, config):
536536 print ("{:<35}: {:<35}" .format (repo_name , repo_hash ))
537537
538538
539+ def merge_no_duplicates (a : dict , b : dict ) -> dict :
540+ result = {** a }
541+ for key , value in b .items ():
542+ if key in a :
543+ raise ValueError (f"Duplicate scheme { key } " )
544+
545+ result [key ] = value
546+ return result
547+
548+
549+ def merge_config (config : dict , new_config : dict ) -> dict :
550+ """
551+ Merge two configs, with a 'last-wins' strategy.
552+
553+ The branch-schemes are rejected if they define duplicate schemes.
554+ """
555+
556+ result = {** config }
557+ for key , value in new_config .items ():
558+ if key == "branch-schemes" :
559+ # We reject duplicates here since this is the most conservative
560+ # behavior, so it can be relaxed in the future.
561+ # TODO: Another semantics might be nicer, define that as it is needed.
562+ result [key ] = merge_no_duplicates (config .get (key , {}), value )
563+ elif key == "repos" :
564+ # The "repos" object is last-wins on a key-by-key basis
565+ result [key ] = {** config .get (key , {}), ** value }
566+ else :
567+ # Anything else is just last-wins
568+ result [key ] = value
569+
570+ return result
571+
572+
539573def validate_config (config ):
540574 # Make sure that our branch-names are unique.
541575 scheme_names = config ['branch-schemes' ].keys ()
@@ -669,9 +703,12 @@ def main():
669703 )
670704 parser .add_argument (
671705 "--config" ,
672- default = os .path .join (SCRIPT_DIR , os .pardir ,
673- "update-checkout-config.json" ),
674- help = "Configuration file to use" )
706+ help = """The configuration file to use. Can be specified multiple times,
707+ each config will be merged together with a 'last-wins' strategy.
708+ Overwriting branch-schemes is not allowed.""" ,
709+ action = "append" ,
710+ default = [],
711+ dest = "configs" )
675712 parser .add_argument (
676713 "--github-comment" ,
677714 help = """Check out related pull requests referenced in the given
@@ -734,8 +771,15 @@ def main():
734771 all_repos = args .all_repositories
735772 use_submodules = args .use_submodules
736773
737- with open (args .config ) as f :
738- config = json .load (f )
774+ # Set the default config path if none are specified
775+ if not args .configs :
776+ default_path = os .path .join (SCRIPT_DIR , os .pardir ,
777+ "update-checkout-config.json" )
778+ args .configs .append (default_path )
779+ config = {}
780+ for config_path in args .configs :
781+ with open (config_path ) as f :
782+ config = merge_config (config , json .load (f ))
739783 validate_config (config )
740784
741785 cross_repos_pr = {}
@@ -783,8 +827,10 @@ def main():
783827 prefix = "[swift] " )
784828
785829 # Re-read the config after checkout.
786- with open (args .config ) as f :
787- config = json .load (f )
830+ config = {}
831+ for config_path in args .configs :
832+ with open (config_path ) as f :
833+ config = merge_config (config , json .load (f ))
788834 validate_config (config )
789835 scheme_map = get_scheme_map (config , scheme_name )
790836
0 commit comments