@@ -257,6 +257,10 @@ local function isHttpsUrl(value)
257257 return type (value ) == " string" and startsWith (value , " https://" ) and not string.find (value , " [\r\n %z]" )
258258end
259259
260+ local function commandSucceeded (status )
261+ return status == 0 or status == true
262+ end
263+
260264local function startsWithPath (value , prefix )
261265 if value == prefix then
262266 return true
@@ -356,14 +360,18 @@ local function buildUvBuildUrl(osType, archType, libc)
356360end
357361
358362local function uvBuildVersion (build )
363+ if build .display_version ~= nil then
364+ return build .display_version
365+ end
359366 if build .variant == " freethreaded" then
360367 return build .version .. " t"
361368 end
362369 return build .version
363370end
364371
365372local function isSupportedUvBuild (build )
366- if build .name ~= " cpython" then
373+ local implementation = build .implementation or build .name
374+ if implementation ~= " cpython" then
367375 return false
368376 end
369377 if build .version == nil then
@@ -372,7 +380,7 @@ local function isSupportedUvBuild(build)
372380 if build .arch and build .arch .variant ~= nil then
373381 return false
374382 end
375- if build .variant ~= nil and build .variant ~= " freethreaded" then
383+ if build .variant ~= nil and build .variant ~= " default " and build . variant ~= " freethreaded" then
376384 return false
377385 end
378386 if not isHttpsUrl (build .url ) then
@@ -381,6 +389,86 @@ local function isSupportedUvBuild(build)
381389 return true
382390end
383391
392+ local function uvBuildSha256 (build )
393+ if type (build .asset ) ~= " table" or type (build .asset .sha256 ) ~= " string" then
394+ return nil
395+ end
396+
397+ local sha256 = string.lower (build .asset .sha256 )
398+ if string.match (sha256 , " ^[0-9a-f]+$" ) and string.len (sha256 ) == 64 then
399+ return sha256
400+ end
401+
402+ error (" Invalid uv-build sha256 for " .. build .url )
403+ end
404+
405+ local function uvBuildAssetName (build )
406+ if type (build .filename ) == " string" then
407+ return string.lower (build .filename )
408+ end
409+ if type (build .url ) == " string" then
410+ return string.lower (build .url )
411+ end
412+ return " "
413+ end
414+
415+ local function uvBuildAssetPriority (build )
416+ local name = uvBuildAssetName (build )
417+ if string.find (name , " pgo+lto-full" , 1 , true ) or string.find (name , " pgo%2blto-full" , 1 , true ) then
418+ return 10
419+ end
420+ if string.find (name , " install_only_stripped" , 1 , true ) then
421+ return 20
422+ end
423+ if string.find (name , " install_only" , 1 , true ) then
424+ return 30
425+ end
426+ if not string.find (name , " debug" , 1 , true ) then
427+ return 40
428+ end
429+ return 90
430+ end
431+
432+ local function verifyUvBuildArchive (path , sha256 )
433+ if sha256 == nil then
434+ return
435+ end
436+
437+ print (" Verifying Python uv-build archive sha256..." )
438+
439+ local status
440+ if RUNTIME .osType == " windows" or OS_TYPE == " windows" then
441+ local command = ' certutil -hashfile ' .. shellQuote (path ) .. ' SHA256 | findstr /i /c:"' .. sha256 .. ' " > NUL'
442+ status = os.execute (command )
443+ else
444+ local command = nil
445+ local hasSha256sum = io.popen (" command -v sha256sum 2>/dev/null" )
446+ local sha256sumPath = hasSha256sum and hasSha256sum :read (" *l" ) or nil
447+ if hasSha256sum then
448+ hasSha256sum :close ()
449+ end
450+
451+ local hasShasum = io.popen (" command -v shasum 2>/dev/null" )
452+ local shasumPath = hasShasum and hasShasum :read (" *l" ) or nil
453+ if hasShasum then
454+ hasShasum :close ()
455+ end
456+
457+ if sha256sumPath and sha256sumPath ~= " " then
458+ command = " printf '%s %s\n ' " .. shellQuote (sha256 ) .. " " .. shellQuote (path ) .. " | sha256sum -c -"
459+ elseif shasumPath and shasumPath ~= " " then
460+ command = " printf '%s %s\n ' " .. shellQuote (sha256 ) .. " " .. shellQuote (path ) .. " | shasum -a 256 -c -"
461+ else
462+ error (" Unable to verify uv-build archive sha256: sha256sum or shasum is required" )
463+ end
464+ status = os.execute (command )
465+ end
466+
467+ if not commandSucceeded (status ) then
468+ error (" uv-build archive sha256 verification failed" )
469+ end
470+ end
471+
384472local function uvBuildDownloadUrl (build )
385473 local mirror = os.getenv (UV_BUILD_MIRROR_ENV )
386474 if mirror == nil or mirror == " " then
@@ -410,16 +498,25 @@ local function getUvBuilds(osType, archType, libc)
410498 end
411499
412500 local jsonObj = json .decode (resp .body )
413- return jsonObj .versions or {}
501+ return jsonObj .items or jsonObj . versions or {}
414502end
415503
416504local function findUvBuild (version )
417505 local osType , archType , libc = getUvBuildPlatform ()
506+ local selectedBuild = nil
507+ local selectedPriority = nil
418508 for _ , build in ipairs (getUvBuilds (osType , archType , libc )) do
419509 if isSupportedUvBuild (build ) and uvBuildVersion (build ) == version then
420- return build
510+ local priority = uvBuildAssetPriority (build )
511+ if selectedBuild == nil or priority < selectedPriority then
512+ selectedBuild = build
513+ selectedPriority = priority
514+ end
421515 end
422516 end
517+ if selectedBuild ~= nil then
518+ return selectedBuild
519+ end
423520 error (" No uv-build prebuilt Python found for version " .. version .. " on " .. osType .. " /" .. archType .. " /" .. libc )
424521end
425522
@@ -504,6 +601,7 @@ function uvBuildInstall(ctx)
504601 if err ~= nil then
505602 error (" Downloading uv-build archive failed" )
506603 end
604+ verifyUvBuildArchive (archivePath , uvBuildSha256 (build ))
507605
508606 print (" Extracting Python uv-build archive..." )
509607 local status = os.execute (" tar -xf " .. shellQuote (archivePath ) .. " --strip-components=1 -C " .. shellQuote (path ))
0 commit comments