@@ -2,23 +2,24 @@ import { ParameterSetting, Plan, StatefulParameter, getPty } from '@codifycli/pl
22
33import { AptConfig } from './apt.js' ;
44
5- export interface AptPackage {
6- name : string ;
7- version ?: string ;
5+ // Extracts the package name without the version specifier (e.g. "nodejs=20.*" → "nodejs")
6+ function packageName ( pkg : string ) : string {
7+ const eqIndex = pkg . indexOf ( '=' )
8+ return eqIndex > 0 ? pkg . slice ( 0 , eqIndex ) : pkg
89}
910
10- export class AptInstallParameter extends StatefulParameter < AptConfig , Array < AptPackage | string > > {
11+ export class AptInstallParameter extends StatefulParameter < AptConfig , string [ ] > {
1112
1213 getSettings ( ) : ParameterSetting {
1314 return {
1415 type : 'array' ,
1516 filterInStatelessMode : ( desired , current ) =>
16- current . filter ( ( c ) => desired . some ( ( d ) => this . isSamePackage ( d , c ) ) ) ,
17+ current . filter ( ( c ) => desired . some ( ( d ) => packageName ( d ) === packageName ( c ) ) ) ,
1718 isElementEqual : this . isEqual ,
1819 }
1920 }
2021
21- async refresh ( desired : Array < AptPackage | string > | null , _config : Partial < AptConfig > ) : Promise < Array < AptPackage | string > | null > {
22+ async refresh ( desired : string [ ] | null , _config : Partial < AptConfig > ) : Promise < string [ ] | null > {
2223 const $ = getPty ( )
2324 const { data : installed } = await $ . spawnSafe ( 'dpkg-query -W -f=\'${Package} ${Version}\\n\'' ) ;
2425
@@ -29,51 +30,36 @@ export class AptInstallParameter extends StatefulParameter<AptConfig, Array<AptP
2930 const r = installed . split ( / \n / )
3031 . filter ( Boolean )
3132 . map ( ( l ) => {
32- const [ name , version ] = l . split ( / \s + / )
33- . filter ( Boolean )
34-
33+ const [ name , version ] = l . split ( / \s + / ) . filter ( Boolean )
3534 return { name, version }
3635 } )
37- . filter ( ( pkg ) =>
38- // Only return packages that are in the desired list
39- desired ?. some ( ( d ) => {
40- if ( typeof d === 'string' ) {
41- return d === pkg . name ;
42- }
43-
44- return d . name === pkg . name ;
45- } )
46- )
47- . map ( ( installed ) => {
48- if ( desired ?. find ( ( d ) => typeof d === 'string' && d === installed . name ) ) {
49- return installed . name ;
50- }
51-
52- if ( desired ?. find ( ( d ) => typeof d === 'object' && d . name === installed . name && ! d . version ) ) {
53- return { name : installed . name }
36+ . filter ( ( pkg ) => desired ?. some ( ( d ) => packageName ( d ) === pkg . name ) )
37+ . map ( ( pkg ) => {
38+ // If desired entry has a version specifier, return name=version so equality checks work
39+ if ( desired ?. some ( ( d ) => d . includes ( '=' ) && packageName ( d ) === pkg . name ) ) {
40+ return `${ pkg . name } =${ pkg . version } `
5441 }
55-
56- return installed ;
42+ return pkg . name
5743 } )
5844
5945 return r . length > 0 ? r : null ;
6046 }
6147
62- async add ( valueToAdd : Array < AptPackage | string > , plan : Plan < AptConfig > ) : Promise < void > {
48+ async add ( valueToAdd : string [ ] , plan : Plan < AptConfig > ) : Promise < void > {
6349 await this . updateIfNeeded ( plan ) ;
6450 await this . install ( valueToAdd ) ;
6551 }
6652
67- async modify ( newValue : ( AptPackage | string ) [ ] , previousValue : ( AptPackage | string ) [ ] , plan : Plan < AptConfig > ) : Promise < void > {
68- const valuesToAdd = newValue . filter ( ( n ) => ! previousValue . some ( ( p ) => this . isSamePackage ( n , p ) ) ) ;
69- const valuesToRemove = previousValue . filter ( ( p ) => ! newValue . some ( ( n ) => this . isSamePackage ( n , p ) ) ) ;
53+ async modify ( newValue : string [ ] , previousValue : string [ ] , plan : Plan < AptConfig > ) : Promise < void > {
54+ const valuesToAdd = newValue . filter ( ( n ) => ! previousValue . some ( ( p ) => packageName ( n ) === packageName ( p ) ) ) ;
55+ const valuesToRemove = previousValue . filter ( ( p ) => ! newValue . some ( ( n ) => packageName ( n ) === packageName ( p ) ) ) ;
7056
7157 await this . uninstall ( valuesToRemove ) ;
7258 await this . updateIfNeeded ( plan ) ;
7359 await this . install ( valuesToAdd ) ;
7460 }
7561
76- async remove ( valueToRemove : ( AptPackage | string ) [ ] , _plan : Plan < AptConfig > ) : Promise < void > {
62+ async remove ( valueToRemove : string [ ] , _plan : Plan < AptConfig > ) : Promise < void > {
7763 await this . uninstall ( valueToRemove ) ;
7864 }
7965
@@ -86,90 +72,35 @@ export class AptInstallParameter extends StatefulParameter<AptConfig, Array<AptP
8672 await $ . spawn ( 'apt-get update' , { requiresRoot : true , interactive : true } ) ;
8773 }
8874
89- private async install ( packages : Array < AptPackage | string > ) : Promise < void > {
75+ private async install ( packages : string [ ] ) : Promise < void > {
9076 if ( ! packages || packages . length === 0 ) {
9177 return ;
9278 }
9379
9480 const $ = getPty ( ) ;
95- const toInstall = packages . map ( ( p ) => {
96- if ( typeof p === 'string' ) {
97- return p ;
98- }
99-
100- if ( p . version ) {
101- return `${ p . name } =${ p . version } ` ;
102- }
103-
104- return p . name ;
105- } ) . join ( ' ' ) ;
106-
107- await $ . spawn ( `apt-get install -y ${ toInstall } ` , {
81+ await $ . spawn ( `apt-get install -y ${ packages . join ( ' ' ) } ` , {
10882 requiresRoot : true ,
10983 env : { DEBIAN_FRONTEND : 'noninteractive' , NEEDRESTART_MODE : 'a' }
11084 } ) ;
11185 }
11286
113- private async uninstall ( packages : Array < AptPackage | string > ) : Promise < void > {
87+ private async uninstall ( packages : string [ ] ) : Promise < void > {
11488 if ( ! packages || packages . length === 0 ) {
11589 return ;
11690 }
11791
11892 const $ = getPty ( ) ;
119- const toUninstall = packages . map ( ( p ) => {
120- if ( typeof p === 'string' ) {
121- return p ;
122- }
123-
124- return p . name ;
125- } ) . join ( ' ' ) ;
126-
127- await $ . spawn ( `apt-get auto-remove -y ${ toUninstall } ` , { requiresRoot : true , env : { DEBIAN_FRONTEND : 'noninteractive' , NEEDRESTART_MODE : 'a' } } ) ;
128- }
129-
130- isSamePackage ( a : AptPackage | string , b : AptPackage | string ) : boolean {
131- if ( typeof a === 'string' || typeof b === 'string' ) {
132- if ( typeof a === 'string' && typeof b === 'string' ) {
133- return a === b ;
134- }
135-
136- if ( typeof a === 'string' && typeof b === 'object' ) {
137- return a === b . name ;
138- }
139-
140- if ( typeof a === 'object' && typeof b === 'string' ) {
141- return a . name === b ;
142- }
143- }
144-
145- if ( typeof a === 'object' && typeof b === 'object' ) {
146- return a . name === b . name ;
147- }
148-
149- return false ;
93+ await $ . spawn ( `apt-get auto-remove -y ${ packages . map ( packageName ) . join ( ' ' ) } ` , {
94+ requiresRoot : true ,
95+ env : { DEBIAN_FRONTEND : 'noninteractive' , NEEDRESTART_MODE : 'a' }
96+ } ) ;
15097 }
15198
152- isEqual ( desired : AptPackage | string , current : AptPackage | string ) : boolean {
153- if ( typeof desired === 'string' || typeof current === 'string' ) {
154- if ( typeof desired === 'string' && typeof current === 'string' ) {
155- return desired === current ;
156- }
157-
158- if ( typeof desired === 'string' && typeof current === 'object' ) {
159- return desired === current . name ;
160- }
161-
162- if ( typeof desired === 'object' && typeof current === 'string' ) {
163- return desired . name === current ;
164- }
165- }
166-
167- if ( typeof desired === 'object' && typeof current === 'object' ) {
168- return desired . version
169- ? desired . version === current . version && desired . name === current . name
170- : desired . name === current . name ;
99+ isEqual ( desired : string , current : string ) : boolean {
100+ // If no version specified in desired, match by name only
101+ if ( ! desired . includes ( '=' ) ) {
102+ return packageName ( desired ) === packageName ( current )
171103 }
172-
173- return false ;
104+ return desired === current
174105 }
175- }
106+ }
0 commit comments