@@ -1071,7 +1071,7 @@ echo "Successfully created image for versioned extension '$EXT_NAME-$EXT_VERSION
10711071 "Step 1/4: Analyzing runtime dependencies" ,
10721072 OutputLevel :: Normal ,
10731073 ) ;
1074- let required_extensions = self . find_extensions_for_runtime ( config, & runtime_config) ?;
1074+ let required_extensions = self . find_extensions_for_runtime ( config, parsed , & runtime_config, target ) ?;
10751075
10761076 // Note: SDK compile sections are now compiled on-demand when extensions are built
10771077 // This prevents duplicate compilation when sdk.compile sections are also extension dependencies
@@ -1252,12 +1252,62 @@ echo "Successfully created image for versioned extension '$EXT_NAME-$EXT_VERSION
12521252 fn find_extensions_for_runtime (
12531253 & self ,
12541254 config : & Config ,
1255+ parsed : & serde_yaml:: Value ,
12551256 runtime_config : & serde_yaml:: Value ,
1257+ target : & str ,
12561258 ) -> Result < Vec < ExtensionDependency > > {
1259+ use crate :: utils:: interpolation:: interpolate_name;
1260+
12571261 let mut required_extensions = HashSet :: new ( ) ;
12581262 let mut visited = HashSet :: new ( ) ;
12591263
1260- // Check runtime dependencies for extensions
1264+ // Build a map of interpolated ext names to their source config
1265+ // This is needed because ext section keys may contain templates like {{ avocado.target }}
1266+ let mut ext_sources: std:: collections:: HashMap < String , Option < ExtensionSource > > =
1267+ std:: collections:: HashMap :: new ( ) ;
1268+ if let Some ( ext_section) = parsed. get ( "extensions" ) . and_then ( |e| e. as_mapping ( ) ) {
1269+ for ( ext_key, ext_config) in ext_section {
1270+ if let Some ( raw_name) = ext_key. as_str ( ) {
1271+ // Interpolate the extension name with the target
1272+ let interpolated_name = interpolate_name ( raw_name, target) ;
1273+ // Use parse_extension_source which properly deserializes the source field
1274+ let source = Config :: parse_extension_source ( & interpolated_name, ext_config)
1275+ . ok ( )
1276+ . flatten ( ) ;
1277+ ext_sources. insert ( interpolated_name, source) ;
1278+ }
1279+ }
1280+ }
1281+
1282+ // Check extensions from the new `extensions` array format
1283+ if let Some ( extensions) = runtime_config. get ( "extensions" ) . and_then ( |e| e. as_sequence ( ) ) {
1284+ for ext in extensions {
1285+ if let Some ( ext_name) = ext. as_str ( ) {
1286+ // Check if this extension has a source: field (remote extension)
1287+ if let Some ( Some ( source) ) = ext_sources. get ( ext_name) {
1288+ // Remote extension with source field
1289+ required_extensions. insert ( ExtensionDependency :: Remote {
1290+ name : ext_name. to_string ( ) ,
1291+ source : source. clone ( ) ,
1292+ } ) ;
1293+ } else {
1294+ // Local extension (defined in ext section without source, or not in ext section)
1295+ required_extensions. insert ( ExtensionDependency :: Local ( ext_name. to_string ( ) ) ) ;
1296+
1297+ // Also check local extension dependencies
1298+ self . find_local_extension_dependencies (
1299+ config,
1300+ parsed,
1301+ ext_name,
1302+ & mut required_extensions,
1303+ & mut visited,
1304+ ) ?;
1305+ }
1306+ }
1307+ }
1308+ }
1309+
1310+ // Check runtime dependencies for extensions (old packages format for backwards compatibility)
12611311 if let Some ( dependencies) = runtime_config. get ( "packages" ) . and_then ( |d| d. as_mapping ( ) ) {
12621312 for ( _dep_name, dep_spec) in dependencies {
12631313 // Check for extension dependency
0 commit comments