@@ -28,13 +28,15 @@ export async function filterAndQualifyAptPackages(packages: AptPackage[], apt: s
2828/**
2929 * Qualify the package into full package name/version.
3030 * If the package is not installed, return the full package name/version.
31- * If the package is already installed, return undefined
31+ * If the package is already installed and upgrade is not requested , return undefined
3232 */
3333export async function qualifiedNeededAptPackage ( pack : AptPackage , apt : string = getApt ( ) ) {
34+ // By default, leave the package in the install list so apt can select the candidate.
35+ const upgrade = pack . upgrade ?? true
3436 // Qualify the package into full package name/version
3537 const qualified = await getAptArg ( apt , pack )
36- // filter out the package that are already installed
37- return ( await isAptPackInstalled ( qualified ) ) ? undefined : qualified
38+ // Filter out packages that are already installed unless they should be upgraded.
39+ return ( await isAptPackInstalled ( qualified ) ) && ! upgrade ? undefined : qualified
3840}
3941
4042async function aptPackageType (
@@ -118,7 +120,14 @@ async function aptCacheShowHasPackage(apt: string, arg: string) {
118120}
119121
120122async function getAptArg ( apt : string , pack : AptPackage ) {
121- const { name, version, fallBackToLatest = false } = pack
123+ const { name, version, upgrade = true , fallBackToLatest = false } = pack
124+
125+ if ( ( version === undefined || version === "" ) && upgrade ) {
126+ const numericVariant = await findHighestNumericAptPackage ( apt , name )
127+ if ( numericVariant !== undefined ) {
128+ return numericVariant
129+ }
130+ }
122131
123132 const package_type = await aptPackageType ( apt , name , version , fallBackToLatest )
124133 switch ( package_type ) {
@@ -133,3 +142,24 @@ async function getAptArg(apt: string, pack: AptPackage) {
133142 throw new Error ( `Could not find package '${ name } ' ${ version ?? "with unspecified version" } ` )
134143 }
135144}
145+
146+ async function findHighestNumericAptPackage ( apt : string , name : string ) {
147+ const packageNamePattern = new RegExp ( `^${ escapeRegex ( name ) } -([0-9]+)$` , "u" )
148+
149+ try {
150+ const { stdout } = await execa ( "apt-cache" , [
151+ "search" ,
152+ "--names-only" ,
153+ `^${ escapeRegex ( name ) } -[0-9]+$` ,
154+ ] , { env : getAptEnv ( apt ) , stdio : "pipe" } )
155+ const candidates = stdout . split ( "\n" ) . flatMap ( ( line ) => {
156+ const packageName : string | undefined = line . trim ( ) . split ( / \s + / u) [ 0 ]
157+ const match = packageName . match ( packageNamePattern )
158+ return match === null ? [ ] : [ { packageName, version : Number . parseInt ( match [ 1 ] , 10 ) } ]
159+ } )
160+
161+ return candidates . sort ( ( first , second ) => second . version - first . version ) [ 0 ] ?. packageName
162+ } catch {
163+ return undefined
164+ }
165+ }
0 commit comments