penguins-eggs
Version:
A remaster system tool, compatible with Arch, Debian, Devuan, Ubuntu and others
414 lines (413 loc) • 14.5 kB
JavaScript
/**
* penguins-eggs - Refactored distros.ts
* class: distro.ts
* author: Piero Proietti
* email: piero.proietti@gmail.com
* license: MIT
*/
import yaml from 'js-yaml';
import fs from 'node:fs';
import path from 'node:path';
import shell from 'shelljs';
import Utils from './utils.js';
import Diversions from './diversions.js';
// _dirname
const __dirname = path.dirname(new URL(import.meta.url).pathname);
/**
* Default configurations for each family
*/
const FAMILY_CONFIGS = {
debian: {
familyId: 'debian',
distroLike: 'Debian',
codenameId: '',
codenameLikeId: '',
liveMediumPath: '/run/live/medium/',
isCalamaresAvailable: true
},
archlinux: {
familyId: 'archlinux',
distroLike: 'Arch',
codenameId: 'rolling',
codenameLikeId: 'rolling',
liveMediumPath: '/run/archiso/bootmnt/',
squashfs: 'arch/x86_64/airootfs.sfs'
},
fedora: {
familyId: 'fedora',
distroLike: 'Fedora',
codenameId: 'rolling',
codenameLikeId: 'fedora',
liveMediumPath: '/run/initramfs/live/'
},
alpine: {
familyId: 'alpine',
distroLike: 'Alpine',
codenameId: 'rolling',
codenameLikeId: 'alpine',
liveMediumPath: '/mnt/'
},
opensuse: {
familyId: 'opensuse',
distroLike: 'Opensuse',
codenameId: 'rolling',
codenameLikeId: 'opensuse',
liveMediumPath: '/run/initramfs/live/',
usrLibPath: '/usr/lib64/'
},
openmamba: {
familyId: 'openmamba',
distroLike: 'openmamba',
codenameId: 'rolling',
codenameLikeId: 'openmamba',
liveMediumPath: '/run/initramfs/live/'
},
aldus: {
familyId: 'aldus',
distroLike: 'Aldus',
codenameId: 'rolling',
codenameLikeId: 'aldus',
liveMediumPath: '/run/initramfs/live/'
},
voidlinux: {
familyId: 'voidlinux',
distroLike: 'Voidlinux',
codenameId: 'rolling',
codenameLikeId: 'voidlinux',
liveMediumPath: '/run/initramfs/live/'
}
};
/**
* Special codename configurations
*/
const CODENAME_CONFIGS = {
// Debian versions
jessie: { distroLike: 'Debian', codenameLikeId: 'jessie', liveMediumPath: '/lib/live/mount/medium/', isCalamaresAvailable: false },
stretch: { distroLike: 'Debian', codenameLikeId: 'stretch', liveMediumPath: '/lib/live/mount/medium/', isCalamaresAvailable: false },
buster: { distroLike: 'Debian', codenameLikeId: 'buster' },
bullseye: { distroLike: 'Debian', codenameLikeId: 'bullseye' },
bookworm: { distroLike: 'Debian', codenameLikeId: 'bookworm' },
trixie: { distroLike: 'Debian', codenameLikeId: 'trixie' },
// Devuan versions
beowulf: { distroLike: 'Devuan', codenameLikeId: 'beowulf' },
chimaera: { distroLike: 'Devuan', codenameLikeId: 'chimaera' },
daedalus: { distroLike: 'Devuan', codenameLikeId: 'daedalus' },
excalibur: { distroLike: 'Devuan', codenameLikeId: 'excalibur' },
// Ubuntu versions
bionic: { distroLike: 'Ubuntu', codenameLikeId: 'bionic', liveMediumPath: '/lib/live/mount/medium/' },
focal: { distroLike: 'Ubuntu', codenameLikeId: 'focal' },
jammy: { distroLike: 'Ubuntu', codenameLikeId: 'jammy' },
noble: { distroLike: 'Ubuntu', codenameLikeId: 'noble' },
devel: { distroLike: 'Ubuntu', codenameLikeId: 'devel' }
};
/**
* Distribution family detector
*/
class DistroDetector {
static detectByDistroId(distroId) {
const fedoraDistros = ['Almalinux', 'Fedora', 'Nobara', 'Rocky'];
if (distroId === 'Alpine')
return 'alpine';
if (fedoraDistros.includes(distroId))
return 'fedora';
if (distroId === 'Openmamba')
return 'openmamba';
if (distroId.includes('Opensuse'))
return 'opensuse';
if (distroId === 'Voidlinux')
return 'voidlinux';
if (distroId === 'Aldus')
return 'aldus';
return null;
}
static detectByCodename(codenameId) {
if (codenameId === 'rolling' || codenameId === 'n/a')
return 'archlinux';
if (CODENAME_CONFIGS[codenameId])
return 'debian'; // All debian-family codenames
return null;
}
static detectFromDerivatives(distroId, codenameId) {
// Check debian/arch derivatives
const derivativesPath = this.getDerivativesPath('derivatives.yaml');
if (fs.existsSync(derivativesPath)) {
const result = this.searchInDerivatives(derivativesPath, codenameId);
if (result)
return result;
}
// Check fedora derivatives
const fedoraDerivativesPath = this.getDerivativesPath('derivatives_fedora.yaml');
if (fs.existsSync(fedoraDerivativesPath)) {
const result = this.searchInFedoraDerivatives(fedoraDerivativesPath, distroId);
if (result)
return result;
}
return null;
}
static getDerivativesPath(filename) {
const userPath = `/etc/penguins-eggs.d/${filename}`;
const defaultPath = path.resolve(__dirname, `../../conf/${filename}`);
return fs.existsSync(userPath) ? userPath : defaultPath;
}
static searchInDerivatives(filePath, codenameId) {
try {
const content = fs.readFileSync(filePath, 'utf8');
const distros = yaml.load(content);
for (const distro of distros) {
if (distro.ids?.includes(codenameId)) {
return {
familyId: distro.family,
config: {
distroLike: distro.distroLike,
codenameLikeId: distro.id,
familyId: distro.family
}
};
}
}
}
catch (error) {
console.warn(`Warning: Could not read derivatives file ${filePath}:`, error);
}
return null;
}
static searchInFedoraDerivatives(filePath, distroId) {
try {
const content = fs.readFileSync(filePath, 'utf8');
const distros = yaml.load(content);
if (distros.includes(distroId)) {
return {
familyId: 'fedora',
config: {
familyId: 'fedora',
distroLike: 'Fedora',
codenameId: 'rolling',
codenameLikeId: 'fedora',
liveMediumPath: '/run/initramfs/live/'
}
};
}
}
catch (error) {
console.warn(`Warning: Could not read fedora derivatives file ${filePath}:`, error);
}
return null;
}
}
/**
* OS Release parser
*/
class OsReleaseParser {
static parseUrls() {
const defaultUrls = {
homeUrl: 'https://penguins-eggs.net',
supportUrl: 'https://penguins-eggs.net',
bugReportUrl: 'https://github.com/pieroproietti/penguins-eggs/issues'
};
const osReleasePath = '/etc/os-release';
if (!fs.existsSync(osReleasePath)) {
return defaultUrls;
}
try {
const data = fs.readFileSync(osReleasePath, 'utf8');
const lines = data.split('\n');
const result = { ...defaultUrls };
for (const line of lines) {
if (line.startsWith('HOME_URL=')) {
result.homeUrl = line.slice('HOME_URL='.length).replaceAll('"', '');
}
else if (line.startsWith('SUPPORT_URL=')) {
result.supportUrl = line.slice('SUPPORT_URL='.length).replaceAll('"', '');
}
else if (line.startsWith('BUG_REPORT_URL=')) {
result.bugReportUrl = line.slice('BUG_REPORT_URL='.length).replaceAll('"', '');
}
}
return result;
}
catch (error) {
console.warn('Warning: Could not parse /etc/os-release:', error);
return defaultUrls;
}
}
}
/**
* Distribution configurator
*/
class DistroConfigurator {
static applySpecialConfigurations(config) {
// Apply family-specific configurations
this.applyFamilySpecificConfig(config);
// Apply distro-specific configurations
this.applyDistroSpecificConfig(config);
}
static applyFamilySpecificConfig(config) {
// Debian family: dynamic usrLibPath
if (config.familyId === 'debian') {
config.usrLibPath = '/usr/lib/' + Utils.usrLibPath();
}
// OpenSUSE: specific usrLibPath
if (config.familyId === 'opensuse') {
config.usrLibPath = '/usr/lib64/';
}
}
static applyDistroSpecificConfig(config) {
// Manjaro-based distributions
if (Diversions.isManjaroBased(config.distroId)) {
config.liveMediumPath = '/run/miso/bootmnt/';
config.squashfs = 'manjaro/x86_64/livefs.sfs';
config.codenameId = shell.exec('lsb_release -cs', { silent: true }).stdout.toString().trim();
}
// BigLinux variants
if (config.distroId.includes('Biglinux')) {
config.distroId = 'Biglinux';
}
if (config.distroId.includes('Bigcommunity')) {
config.distroId = 'Bigcommunity';
}
// Debian sid → trixie
if (config.distroId === 'Debian' && config.codenameId === 'sid') {
config.codenameId = 'trixie';
}
}
}
/**
* Main Distro class - Refactored
*/
class Distro {
bugReportUrl;
codenameId;
codenameLikeId;
distroId;
distroLike;
familyId;
homeUrl;
isCalamaresAvailable;
liveMediumPath;
releaseId;
squashfs;
supportUrl;
syslinuxPath;
usrLibPath;
/**
* Constructor - Now much cleaner and focused
*/
constructor() {
// Initialize with defaults
this.initializeDefaults();
// Get OS information
const osInfo = Utils.getOsRelease();
this.distroId = osInfo.ID;
this.codenameId = osInfo.VERSION_CODENAME;
this.releaseId = osInfo.VERSION_ID;
// Configure the distribution
this.configure();
}
/**
* Initialize with default values
*/
initializeDefaults() {
const defaultConfig = FAMILY_CONFIGS.debian;
const urls = OsReleaseParser.parseUrls();
Object.assign(this, defaultConfig, urls, {
squashfs: 'live/filesystem.squashfs',
syslinuxPath: path.resolve(__dirname, '../../syslinux'),
usrLibPath: '/usr/lib',
isCalamaresAvailable: true
});
}
/**
* Main configuration logic
*/
configure() {
try {
// Step 1: Detect family by distro ID
let familyId = DistroDetector.detectByDistroId(this.distroId);
// Step 2: If not found, detect by codename
if (!familyId) {
familyId = DistroDetector.detectByCodename(this.codenameId);
}
// Step 3: Apply family configuration
if (familyId && FAMILY_CONFIGS[familyId]) {
this.applyFamilyConfig(familyId);
}
else {
// Step 4: Check derivatives
const derivativeResult = DistroDetector.detectFromDerivatives(this.distroId, this.codenameId);
if (derivativeResult) {
this.applyCustomConfig(derivativeResult.familyId, derivativeResult.config);
}
else {
this.handleUnknownDistro();
return;
}
}
// Step 5: Apply codename-specific configurations
this.applyCodenameConfig();
// Step 6: Apply special configurations
DistroConfigurator.applySpecialConfigurations(this);
}
catch (error) {
console.error('Error during distro configuration:', error);
this.handleUnknownDistro();
}
}
/**
* Apply family-based configuration
*/
applyFamilyConfig(familyId) {
const config = FAMILY_CONFIGS[familyId];
this.familyId = config.familyId;
this.distroLike = config.distroLike;
if (config.codenameId)
this.codenameId = config.codenameId;
if (config.codenameLikeId)
this.codenameLikeId = config.codenameLikeId;
if (config.liveMediumPath)
this.liveMediumPath = config.liveMediumPath;
if (config.squashfs)
this.squashfs = config.squashfs;
if (config.usrLibPath)
this.usrLibPath = config.usrLibPath;
if (config.isCalamaresAvailable !== undefined)
this.isCalamaresAvailable = config.isCalamaresAvailable;
}
/**
* Apply custom configuration from derivatives
*/
applyCustomConfig(familyId, config) {
this.familyId = familyId;
// Apply base family config first
if (FAMILY_CONFIGS[familyId]) {
this.applyFamilyConfig(familyId);
}
// Then override with custom config
Object.assign(this, config);
}
/**
* Apply codename-specific configurations
*/
applyCodenameConfig() {
const codenameConfig = CODENAME_CONFIGS[this.codenameId];
if (codenameConfig) {
Object.assign(this, codenameConfig);
// Set familyId to debian for debian-family codenames
if (!this.familyId || this.familyId === 'debian') {
this.familyId = 'debian';
}
}
}
/**
* Handle unknown distribution
*/
handleUnknownDistro() {
console.log(`This distro ${this.distroId}/${this.codenameId} is not yet recognized!`);
console.log('');
console.log('You can edit /usr/lib/penguins-eggs/conf/derivatives.yaml to add it -');
console.log('after that - run: sudo eggs dad -d to re-configure eggs.');
console.log('If you can create your new iso, you can contribute to the project');
console.log('by suggesting your modification.');
process.exit(0);
}
}
export default Distro;