Skip to content

Commit 108222b

Browse files
committed
feat: improved installViaPackageMgr
1 parent 527766a commit 108222b

3 files changed

Lines changed: 116 additions & 28 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@codifycli/plugin-core",
3-
"version": "1.2.3",
3+
"version": "1.2.5",
44
"description": "TypeScript library for building Codify plugins to manage system resources (applications, CLI tools, settings) through infrastructure-as-code",
55
"main": "dist/index.js",
66
"typings": "dist/index.d.ts",

src/utils/index.ts

Lines changed: 113 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
2757
export 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

Comments
 (0)