1414from packaging .version import Version
1515
1616CI_TARGETS_YAML = "ci-targets.yaml"
17+ CI_RUNNERS_YAML = "ci-runners.yaml"
1718CI_EXTRA_SKIP_LABELS = ["documentation" ]
1819
1920
@@ -88,6 +89,7 @@ def should_include_entry(entry: dict[str, str], filters: dict[str, set[str]]) ->
8889
8990def generate_matrix_entries (
9091 config : dict [str , Any ],
92+ runners : dict [str , Any ],
9193 platform_filter : Optional [str ] = None ,
9294 label_filters : Optional [dict [str , set [str ]]] = None ,
9395) -> list [dict [str , str ]]:
@@ -103,6 +105,7 @@ def generate_matrix_entries(
103105 target_triple ,
104106 target_config ,
105107 platform ,
108+ runners ,
106109 label_filters .get ("directives" , set ()),
107110 )
108111
@@ -117,22 +120,50 @@ def generate_matrix_entries(
117120 return matrix_entries
118121
119122
123+ def find_runner (runners : dict [str , Any ], platform : str , arch : str ) -> str :
124+ # Find a matching platform first
125+ match_platform = [
126+ runner for runner in runners if runners [runner ]["platform" ] == platform
127+ ]
128+
129+ # Then, find a matching architecture
130+ match_arch = [
131+ runner for runner in match_platform if runners [runner ]["arch" ] == arch
132+ ]
133+
134+ # If there's a matching architecture, use that
135+ if match_arch :
136+ return match_arch [0 ]
137+
138+ # Otherwise, use the first with a matching platform
139+ if match_platform :
140+ return match_platform [0 ]
141+
142+ raise RuntimeError (f"No runner found for platform { platform !r} and arch { arch !r} " )
143+
144+
120145def add_matrix_entries_for_config (
121146 matrix_entries : list [dict [str , str ]],
122147 target_triple : str ,
123148 config : dict [str , Any ],
124149 platform : str ,
150+ runners : dict [str , Any ],
125151 directives : set [str ],
126152) -> None :
127153 python_versions = config ["python_versions" ]
128154 build_options = config ["build_options" ]
129155 arch = config ["arch" ]
156+ runner = find_runner (runners , platform , arch )
130157
131158 # Create base entry that will be used for all variants
132159 base_entry = {
133160 "arch" : arch ,
134161 "target_triple" : target_triple ,
135162 "platform" : platform ,
163+ "runner" : runner ,
164+ # If `run` is in the config, use that — otherwise, default to if the
165+ # runner architecture matches the build architecture
166+ "run" : str (config .get ("run" , runners [runner ]["arch" ] == arch )).lower (),
136167 }
137168
138169 # Add optional fields if they exist
@@ -142,8 +173,6 @@ def add_matrix_entries_for_config(
142173 base_entry ["libc" ] = config ["libc" ]
143174 if "vcvars" in config :
144175 base_entry ["vcvars" ] = config ["vcvars" ]
145- if "run" in config :
146- base_entry ["run" ] = str (config ["run" ]).lower ()
147176
148177 if "dry-run" in directives :
149178 base_entry ["dry-run" ] = "true"
@@ -191,6 +220,11 @@ def parse_args() -> argparse.Namespace:
191220 "--labels" ,
192221 help = "Comma-separated list of labels to filter by (e.g., 'platform:darwin,python:3.13,build:debug'), all must match." ,
193222 )
223+ parser .add_argument (
224+ "--free-runners" ,
225+ action = "store_true" ,
226+ help = "If only free runners should be used." ,
227+ )
194228 return parser .parse_args ()
195229
196230
@@ -201,9 +235,21 @@ def main() -> None:
201235 with open (CI_TARGETS_YAML , "r" ) as f :
202236 config = yaml .safe_load (f )
203237
238+ with open (CI_RUNNERS_YAML , "r" ) as f :
239+ runners = yaml .safe_load (f )
240+
241+ # If only free runners are allowed, reduce to a subset
242+ if args .free_runners :
243+ runners = {
244+ runner : runner_config
245+ for runner , runner_config in runners .items ()
246+ if runner_config .get ("free" )
247+ }
248+
204249 matrix = {
205250 "include" : generate_matrix_entries (
206251 config ,
252+ runners ,
207253 args .platform ,
208254 labels ,
209255 )
0 commit comments