@@ -24,6 +24,36 @@ export interface SystemInfo {
2424 shell : Shell ;
2525}
2626
27+ export enum PackageManager {
28+ BREW = 'brew' ,
29+ APT = 'apt' ,
30+ DNF = 'dnf' ,
31+ YUM = 'yum' ,
32+ PACMAN = 'pacman' ,
33+ }
34+
35+ export interface BasePkgMgrOptions {
36+ flags ?: string [ ] ;
37+ }
38+
39+ export interface BrewOptions extends BasePkgMgrOptions {
40+ cask ?: boolean ;
41+ adopt ?: boolean ;
42+ }
43+
44+ export interface AptOptions extends BasePkgMgrOptions { }
45+ export interface DnfOptions extends BasePkgMgrOptions { }
46+ export interface YumOptions extends BasePkgMgrOptions { }
47+ export interface PacmanOptions extends BasePkgMgrOptions { }
48+
49+ export type PkgMgrOptionsMap = {
50+ [ PackageManager . BREW ] ?: BrewOptions ;
51+ [ PackageManager . APT ] ?: AptOptions ;
52+ [ PackageManager . DNF ] ?: DnfOptions ;
53+ [ PackageManager . YUM ] ?: YumOptions ;
54+ [ PackageManager . PACMAN ] ?: PacmanOptions ;
55+ } ;
56+
2757export const Utils = {
2858 getUser ( ) : string {
2959 return os . userInfo ( ) . username ;
@@ -194,33 +224,50 @@ Brew can be installed using Codify:
194224 } ,
195225
196226 /**
197- * Installs a package via the system package manager. This will use Homebrew on macOS and apt on Ubuntu/Debian or dnf on Fedora.
198- * @param packageName
227+ * Installs a package via the system package manager. Auto-detects the PM from the OS unless
228+ * forcePackageManager is specified. Per-PM options (flags, cask, etc.) can be passed via the
229+ * options map.
199230 */
200- async installViaPkgMgr ( packageName : string ) : Promise < void > {
231+ async installViaPkgMgr (
232+ packageName : string ,
233+ options ?: PkgMgrOptionsMap ,
234+ forcePackageManager ?: PackageManager ,
235+ ) : Promise < void > {
201236 const $ = getPty ( ) ;
202237
203- if ( Utils . isMacOS ( ) ) {
238+ const useBrew = forcePackageManager === PackageManager . BREW || ( ! forcePackageManager && Utils . isMacOS ( ) ) ;
239+ if ( useBrew ) {
204240 await this . assertBrewInstalled ( ) ;
205- await $ . spawn ( `brew install ${ packageName } ` , { interactive : true , env : { HOMEBREW_NO_AUTO_UPDATE : 1 } } ) ;
241+ const brewOpts = options ?. [ PackageManager . BREW ] ;
242+ const flags : string [ ] = [ ] ;
243+ if ( brewOpts ?. cask || brewOpts ?. adopt ) flags . push ( '--cask' ) ;
244+ if ( brewOpts ?. adopt ) flags . push ( '--adopt' ) ;
245+ if ( brewOpts ?. flags ) flags . push ( ...brewOpts . flags ) ;
246+ const flagStr = flags . length > 0 ? `${ flags . join ( ' ' ) } ` : '' ;
247+ await $ . spawn ( `brew install ${ flagStr } ${ packageName } ` , { interactive : true , env : { HOMEBREW_NO_AUTO_UPDATE : 1 , HOMEBREW_NO_ASK : 1 } } ) ;
248+ return ;
206249 }
207250
208- if ( Utils . isLinux ( ) ) {
251+ const useApt = forcePackageManager === PackageManager . APT || ( ! forcePackageManager && Utils . isLinux ( ) ) ;
252+ if ( useApt ) {
253+ const aptOpts = options ?. [ PackageManager . APT ] ;
254+ const extraFlags = aptOpts ?. flags ?? [ ] ;
255+
209256 const isAptInstalled = await $ . spawnSafe ( 'which apt' ) ;
210257 if ( isAptInstalled . status === SpawnStatus . SUCCESS ) {
211258 await $ . spawn ( 'apt-get update' , { requiresRoot : true } ) ;
212- const { status, data } = await $ . spawnSafe ( `apt-get -y -qq install -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0 ${ packageName } ` , {
259+ const flagStr = extraFlags . length > 0 ? `${ extraFlags . join ( ' ' ) } ` : '' ;
260+ const { status, data } = await $ . spawnSafe ( `apt-get -y -qq install -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0 ${ flagStr } ${ packageName } ` , {
213261 requiresRoot : true ,
214- env : { DEBIAN_FRONTEND : 'noninteractive' , NEEDRESTART_MODE : 'a' , }
262+ env : { DEBIAN_FRONTEND : 'noninteractive' , NEEDRESTART_MODE : 'a' }
215263 } ) ;
216264
217265 if ( status === SpawnStatus . ERROR && data . includes ( 'E: dpkg was interrupted, you must manually run \'sudo dpkg --configure -a\' to correct the problem.' ) ) {
218266 await $ . spawn ( 'dpkg --configure -a' , { requiresRoot : true } ) ;
219- await $ . spawn ( `apt-get -y install ${ packageName } ` , {
267+ await $ . spawn ( `apt-get -y install ${ flagStr } ${ packageName } ` , {
220268 requiresRoot : true ,
221269 env : { DEBIAN_FRONTEND : 'noninteractive' , NEEDRESTART_MODE : 'a' }
222270 } ) ;
223-
224271 return ;
225272 }
226273
@@ -235,7 +282,7 @@ Brew can be installed using Codify:
235282 throw new Error ( `Failed to install package ${ packageName } via apt: ${ data } ` ) ;
236283 }
237284
238- const retryResult = await $ . spawnSafe ( `apt-get -y -qq install -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0 ${ packageName } ` , {
285+ const retryResult = await $ . spawnSafe ( `apt-get -y -qq install -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0 ${ flagStr } ${ packageName } ` , {
239286 requiresRoot : true ,
240287 env : { DEBIAN_FRONTEND : 'noninteractive' , NEEDRESTART_MODE : 'a' }
241288 } ) ;
@@ -244,64 +291,105 @@ Brew can be installed using Codify:
244291 throw new Error ( `Failed to install package ${ packageName } via apt after fixing dependencies: ${ retryResult . data } ` ) ;
245292 }
246293 }
294+ return ;
247295 }
296+ }
248297
298+ if ( forcePackageManager === PackageManager . DNF || ! forcePackageManager ) {
299+ const dnfOpts = options ?. [ PackageManager . DNF ] ;
300+ const extraFlags = dnfOpts ?. flags ?? [ ] ;
249301 const isDnfInstalled = await $ . spawnSafe ( 'which dnf' ) ;
250302 if ( isDnfInstalled . status === SpawnStatus . SUCCESS ) {
303+ const flagStr = extraFlags . length > 0 ? `${ extraFlags . join ( ' ' ) } ` : '' ;
251304 await $ . spawn ( 'dnf update' , { requiresRoot : true } ) ;
252- await $ . spawn ( `dnf install ${ packageName } -y` , { requiresRoot : true } ) ;
305+ await $ . spawn ( `dnf install ${ flagStr } ${ packageName } -y` , { requiresRoot : true } ) ;
306+ return ;
253307 }
308+ }
254309
310+ if ( forcePackageManager === PackageManager . YUM || ! forcePackageManager ) {
311+ const yumOpts = options ?. [ PackageManager . YUM ] ;
312+ const extraFlags = yumOpts ?. flags ?? [ ] ;
255313 const isYumInstalled = await $ . spawnSafe ( 'which yum' ) ;
256314 if ( isYumInstalled . status === SpawnStatus . SUCCESS ) {
315+ const flagStr = extraFlags . length > 0 ? `${ extraFlags . join ( ' ' ) } ` : '' ;
257316 await $ . spawn ( 'yum update' , { requiresRoot : true } ) ;
258- await $ . spawn ( `yum install ${ packageName } -y` , { requiresRoot : true } ) ;
317+ await $ . spawn ( `yum install ${ flagStr } ${ packageName } -y` , { requiresRoot : true } ) ;
318+ return ;
259319 }
320+ }
260321
322+ if ( forcePackageManager === PackageManager . PACMAN || ! forcePackageManager ) {
323+ const pacmanOpts = options ?. [ PackageManager . PACMAN ] ;
324+ const extraFlags = pacmanOpts ?. flags ?? [ ] ;
261325 const isPacmanInstalled = await $ . spawnSafe ( 'which pacman' ) ;
262326 if ( isPacmanInstalled . status === SpawnStatus . SUCCESS ) {
327+ const flagStr = extraFlags . length > 0 ? `${ extraFlags . join ( ' ' ) } ` : '' ;
263328 await $ . spawn ( 'pacman -Syu' , { requiresRoot : true } ) ;
264- await $ . spawn ( `pacman -S ${ packageName } --noconfirm` , { requiresRoot : true } ) ;
329+ await $ . spawn ( `pacman -S ${ flagStr } ${ packageName } --noconfirm` , { requiresRoot : true } ) ;
330+ return ;
265331 }
266-
267332 }
268333 } ,
269334
270- async uninstallViaPkgMgr ( packageName : string ) : Promise < boolean > {
335+ async uninstallViaPkgMgr (
336+ packageName : string ,
337+ options ?: PkgMgrOptionsMap ,
338+ forcePackageManager ?: PackageManager ,
339+ ) : Promise < boolean > {
271340 const $ = getPty ( ) ;
272341
273- if ( Utils . isMacOS ( ) ) {
342+ const useBrew = forcePackageManager === PackageManager . BREW || ( ! forcePackageManager && Utils . isMacOS ( ) ) ;
343+ if ( useBrew ) {
274344 await this . assertBrewInstalled ( ) ;
275- const { status } = await $ . spawnSafe ( `brew uninstall --zap ${ packageName } ` , {
345+ const brewOpts = options ?. [ PackageManager . BREW ] ;
346+ const flags : string [ ] = [ ] ;
347+ if ( brewOpts ?. cask || brewOpts ?. adopt ) flags . push ( '--cask' ) ;
348+ if ( brewOpts ?. flags ) flags . push ( ...brewOpts . flags ) ;
349+ flags . push ( '--zap' ) ;
350+ const flagStr = flags . length > 0 ? `${ flags . join ( ' ' ) } ` : '' ;
351+ const { status } = await $ . spawnSafe ( `brew uninstall ${ flagStr } ${ packageName } ` , {
276352 interactive : true ,
277- env : { HOMEBREW_NO_AUTO_UPDATE : 1 }
353+ env : { HOMEBREW_NO_AUTO_UPDATE : 1 , HOMEBREW_NO_ASK : 1 }
278354 } ) ;
279355 return status === SpawnStatus . SUCCESS ;
280356 }
281357
282- if ( Utils . isLinux ( ) ) {
358+ const useApt = forcePackageManager === PackageManager . APT || ( ! forcePackageManager && Utils . isLinux ( ) ) ;
359+ if ( useApt ) {
360+ const aptOpts = options ?. [ PackageManager . APT ] ;
361+ const extraFlags = aptOpts ?. flags ?? [ ] ;
283362 const isAptInstalled = await $ . spawnSafe ( 'which apt' ) ;
284363 if ( isAptInstalled . status === SpawnStatus . SUCCESS ) {
285- const { status } = await $ . spawnSafe ( `apt-get -qq autoremove -y -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0 --purge ${ packageName } ` , {
364+ const flagStr = extraFlags . length > 0 ? `${ extraFlags . join ( ' ' ) } ` : '' ;
365+ const { status } = await $ . spawnSafe ( `apt-get -qq autoremove -y -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0 --purge ${ flagStr } ${ packageName } ` , {
286366 requiresRoot : true ,
287367 env : { DEBIAN_FRONTEND : 'noninteractive' , NEEDRESTART_MODE : 'a' }
288368 } ) ;
289369 return status === SpawnStatus . SUCCESS ;
290370 }
371+ }
291372
373+ if ( forcePackageManager === PackageManager . DNF || ! forcePackageManager ) {
374+ const dnfOpts = options ?. [ PackageManager . DNF ] ;
375+ const extraFlags = dnfOpts ?. flags ?? [ ] ;
292376 const isDnfInstalled = await $ . spawnSafe ( 'which dnf' ) ;
293377 if ( isDnfInstalled . status === SpawnStatus . SUCCESS ) {
294- const { status } = await $ . spawnSafe ( `dnf autoremove ${ packageName } -y` , { requiresRoot : true } ) ;
378+ const flagStr = extraFlags . length > 0 ? `${ extraFlags . join ( ' ' ) } ` : '' ;
379+ const { status } = await $ . spawnSafe ( `dnf autoremove ${ flagStr } ${ packageName } -y` , { requiresRoot : true } ) ;
295380 return status === SpawnStatus . SUCCESS ;
296381 }
382+ }
297383
384+ if ( forcePackageManager === PackageManager . YUM || ! forcePackageManager ) {
385+ const yumOpts = options ?. [ PackageManager . YUM ] ;
386+ const extraFlags = yumOpts ?. flags ?? [ ] ;
298387 const isYumInstalled = await $ . spawnSafe ( 'which yum' ) ;
299388 if ( isYumInstalled . status === SpawnStatus . SUCCESS ) {
300- const { status } = await $ . spawnSafe ( `yum autoremove ${ packageName } -y` , { requiresRoot : true } ) ;
389+ const flagStr = extraFlags . length > 0 ? `${ extraFlags . join ( ' ' ) } ` : '' ;
390+ const { status } = await $ . spawnSafe ( `yum autoremove ${ flagStr } ${ packageName } -y` , { requiresRoot : true } ) ;
301391 return status === SpawnStatus . SUCCESS ;
302392 }
303-
304- return false ;
305393 }
306394
307395 return false ;
0 commit comments