Skip to content

Commit a7c637e

Browse files
committed
feat: Added support for linux distros
1 parent 0e7d449 commit a7c637e

File tree

6 files changed

+180
-12
lines changed

6 files changed

+180
-12
lines changed

package-lock.json

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

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codify-plugin-lib",
3-
"version": "1.0.182-beta57",
3+
"version": "1.0.182-beta66",
44
"description": "Library plugin library",
55
"main": "dist/index.js",
66
"typings": "dist/index.d.ts",
@@ -22,7 +22,7 @@
2222
"ajv": "^8.12.0",
2323
"ajv-formats": "^2.1.1",
2424
"clean-deep": "^3.4.0",
25-
"codify-schemas": "1.0.86-beta10",
25+
"codify-schemas": "1.0.86-beta11",
2626
"lodash.isequal": "^4.5.0",
2727
"nanoid": "^5.0.9",
2828
"strip-ansi": "^7.1.0",

src/plugin/plugin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export class Plugin {
7777
type: r.typeId,
7878
sensitiveParameters,
7979
operatingSystems: r.settings.operatingSystems,
80+
linuxDistros: r.settings.linuxDistros,
8081
}
8182
})
8283
}
@@ -124,6 +125,7 @@ export class Plugin {
124125
requiredParameters: requiredPropertyNames,
125126
},
126127
operatingSystems: resource.settings.operatingSystems,
128+
linuxDistros: resource.settings.linuxDistros,
127129
sensitiveParameters,
128130
allowMultiple
129131
}

src/resource/parsed-resource-settings.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { JSONSchemaType } from 'ajv';
2-
import { OS, StringIndexedObject } from 'codify-schemas';
2+
import { LinuxDistro, OS, StringIndexedObject } from 'codify-schemas';
33
import { ZodObject, z } from 'zod';
44

55
import { StatefulParameterController } from '../stateful-parameter/stateful-parameter-controller.js';
@@ -52,6 +52,8 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
5252
transformation?: InputTransformation;
5353

5454
operatingSystems!: Array<OS>;
55+
linuxDistros?: Array<LinuxDistro>;
56+
5557
isSensitive?: boolean;
5658

5759
private settings: ResourceSettings<T>;

src/resource/resource-settings.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { JSONSchemaType } from 'ajv';
2-
import { OS, StringIndexedObject } from 'codify-schemas';
2+
import { LinuxDistro, OS, StringIndexedObject } from 'codify-schemas';
33
import isObjectsEqual from 'lodash.isequal'
44
import path from 'node:path';
55
import { ZodObject } from 'zod';
@@ -35,6 +35,11 @@ export interface ResourceSettings<T extends StringIndexedObject> {
3535
*/
3636
operatingSystems: Array<OS>;
3737

38+
/**
39+
* List of supported linux distros
40+
*/
41+
linuxDistros?: Array<LinuxDistro>;
42+
3843
/**
3944
* Schema to validate user configs with. Must be in the format JSON Schema draft07
4045
*/

src/utils/index.ts

Lines changed: 161 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { OS } from 'codify-schemas';
1+
import { LinuxDistro, OS } from 'codify-schemas';
2+
import * as fsSync from 'node:fs';
3+
import * as fs from 'node:fs/promises';
24
import os from 'node:os';
35
import path from 'node:path';
46

5-
import { getPty, SpawnStatus } from '../pty/index.js';
7+
import { SpawnStatus, getPty } from '../pty/index.js';
68

79
export function isDebug(): boolean {
810
return process.env.DEBUG != null && process.env.DEBUG.includes('codify'); // TODO: replace with debug library
@@ -175,6 +177,163 @@ export const Utils = {
175177
const lines = pathQuery.split(':');
176178
return lines.includes(directory);
177179
},
180+
181+
async assertBrewInstalled(): Promise<void> {
182+
const $ = getPty();
183+
const brewCheck = await $.spawnSafe('which brew', { interactive: true });
184+
if (brewCheck.status === SpawnStatus.ERROR) {
185+
throw new Error(
186+
`Homebrew is not installed. Cannot install git-lfs without Homebrew installed.
187+
188+
Brew can be installed using Codify:
189+
{
190+
"type": "homebrew",
191+
}`
192+
);
193+
}
194+
},
195+
196+
/**
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
199+
*/
200+
async installViaPkgMgr(packageName: string): Promise<void> {
201+
const $ = getPty();
202+
203+
if (Utils.isMacOS()) {
204+
await this.assertBrewInstalled();
205+
await $.spawn(`brew install ${packageName}`, { interactive: true, env: { HOMEBREW_NO_AUTO_UPDATE: 1 } });
206+
}
207+
208+
if (Utils.isLinux()) {
209+
const isAptInstalled = await $.spawnSafe('which apt');
210+
if (isAptInstalled.status === SpawnStatus.SUCCESS) {
211+
await $.spawn('apt-get update', { requiresRoot: true });
212+
const { status, data } = await $.spawnSafe(`apt-get -y install ${packageName}`, {
213+
requiresRoot: true,
214+
env: { DEBIAN_FRONTEND: 'noninteractive' }
215+
});
216+
217+
if (status === SpawnStatus.ERROR && data.includes('E: dpkg was interrupted, you must manually run \'sudo dpkg --configure -a\' to correct the problem.')) {
218+
await $.spawn('dpkg --configure -a', { requiresRoot: true });
219+
await $.spawn(`apt-get -y install ${packageName}`, {
220+
requiresRoot: true,
221+
env: { DEBIAN_FRONTEND: 'noninteractive' }
222+
});
223+
224+
return;
225+
}
226+
227+
if (status === SpawnStatus.ERROR) {
228+
throw new Error(`Failed to install package ${packageName} via apt: ${data}`);
229+
}
230+
}
231+
232+
const isDnfInstalled = await $.spawnSafe('which dnf');
233+
if (isDnfInstalled.status === SpawnStatus.SUCCESS) {
234+
await $.spawn('dnf update', { requiresRoot: true });
235+
await $.spawn(`dnf install ${packageName} -y`, { requiresRoot: true });
236+
}
237+
238+
const isYumInstalled = await $.spawnSafe('which yum');
239+
if (isYumInstalled.status === SpawnStatus.SUCCESS) {
240+
await $.spawn('yum update', { requiresRoot: true });
241+
await $.spawn(`yum install ${packageName} -y`, { requiresRoot: true });
242+
}
243+
244+
const isPacmanInstalled = await $.spawnSafe('which pacman');
245+
if (isPacmanInstalled.status === SpawnStatus.SUCCESS) {
246+
await $.spawn('pacman -Syu', { requiresRoot: true });
247+
await $.spawn(`pacman -S ${packageName} --noconfirm`, { requiresRoot: true });
248+
}
249+
250+
}
251+
},
252+
253+
async uninstallViaPkgMgr(packageName: string): Promise<boolean> {
254+
const $ = getPty();
255+
256+
if (Utils.isMacOS()) {
257+
await this.assertBrewInstalled();
258+
const { status } = await $.spawnSafe(`brew uninstall --zap ${packageName}`, {
259+
interactive: true,
260+
env: { HOMEBREW_NO_AUTO_UPDATE: 1 }
261+
});
262+
return status === SpawnStatus.SUCCESS;
263+
}
264+
265+
if (Utils.isLinux()) {
266+
const isAptInstalled = await $.spawnSafe('which apt');
267+
if (isAptInstalled.status === SpawnStatus.SUCCESS) {
268+
const { status } = await $.spawnSafe(`apt-get autoremove -y --purge ${packageName}`, {
269+
requiresRoot: true,
270+
env: { DEBIAN_FRONTEND: 'noninteractive' }
271+
});
272+
return status === SpawnStatus.SUCCESS;
273+
}
274+
275+
const isDnfInstalled = await $.spawnSafe('which dnf');
276+
if (isDnfInstalled.status === SpawnStatus.SUCCESS) {
277+
const { status } = await $.spawnSafe(`dnf autoremove ${packageName} -y`, { requiresRoot: true });
278+
return status === SpawnStatus.SUCCESS;
279+
}
280+
281+
const isYumInstalled = await $.spawnSafe('which yum');
282+
if (isYumInstalled.status === SpawnStatus.SUCCESS) {
283+
const { status } = await $.spawnSafe(`yum autoremove ${packageName} -y`, { requiresRoot: true });
284+
return status === SpawnStatus.SUCCESS;
285+
}
286+
287+
return false;
288+
}
289+
290+
return false;
291+
},
292+
293+
async getLinuxDistro(): Promise<LinuxDistro | undefined> {
294+
const osRelease = await fs.readFile('/etc/os-release', 'utf8');
295+
const lines = osRelease.split('\n');
296+
for (const line of lines) {
297+
if (line.startsWith('ID=')) {
298+
const distroId = line.slice(3).trim().replaceAll('"', '');
299+
return Object.values(LinuxDistro).includes(distroId as LinuxDistro) ? distroId as LinuxDistro : undefined;
300+
}
301+
}
302+
303+
return undefined;
304+
},
305+
306+
async isUbuntu(): Promise<boolean> {
307+
return (await this.getLinuxDistro()) === LinuxDistro.UBUNTU;
308+
},
309+
310+
async isDebian(): Promise<boolean> {
311+
return (await this.getLinuxDistro()) === LinuxDistro.DEBIAN;
312+
},
313+
314+
async isArch(): Promise<boolean> {
315+
return (await this.getLinuxDistro()) === LinuxDistro.ARCH;
316+
},
317+
318+
async isCentOS(): Promise<boolean> {
319+
return (await this.getLinuxDistro()) === LinuxDistro.CENTOS;
320+
},
321+
322+
async isFedora(): Promise<boolean> {
323+
return (await this.getLinuxDistro()) === LinuxDistro.FEDORA;
324+
},
325+
326+
async isRHEL(): Promise<boolean> {
327+
return (await this.getLinuxDistro()) === LinuxDistro.RHEL;
328+
},
329+
330+
isDebianBased(): boolean {
331+
return fsSync.existsSync('/etc/debian_version');
332+
},
333+
334+
isRedhatBased(): boolean {
335+
return fsSync.existsSync('/etc/redhat-release');
336+
}
178337
};
179338

180339

0 commit comments

Comments
 (0)