penguins-eggs 25.11.29 → 25.12.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/.oclif.manifest.json +1 -1
  2. package/README.md +118 -127
  3. package/README.pdf +10950 -11893
  4. package/addons/eggs/theme/livecd/simple.grub.main.cfg +3 -3
  5. package/conf/derivatives.yaml +2 -1
  6. package/conf/distros/buster/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
  7. package/conf/distros/focal/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
  8. package/conf/distros/noble/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
  9. package/conf/distros/noble/calamares/libexec/calamares-l10n-helper.sh +2 -1
  10. package/conf/distros/noble/calamares/settings.yml +1 -0
  11. package/conf/distros/trixie/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
  12. package/conf/exclude.list.d/var.list +11 -6
  13. package/dist/appimage/dependency-manager.js +1 -1
  14. package/dist/classes/cli-autologin.d.ts +37 -4
  15. package/dist/classes/cli-autologin.js +153 -115
  16. package/dist/classes/compressors.d.ts +7 -10
  17. package/dist/classes/compressors.js +44 -31
  18. package/dist/classes/daddy.js +4 -1
  19. package/dist/classes/distro.js +2 -2
  20. package/dist/classes/diversions.js +2 -3
  21. package/dist/classes/incubation/fisherman-helper/initcpio.d.ts +3 -5
  22. package/dist/classes/incubation/fisherman-helper/initcpio.js +28 -20
  23. package/dist/classes/incubation/fisherman-helper/settings.js +1 -1
  24. package/dist/classes/incubation/fisherman.js +1 -1
  25. package/dist/classes/incubation/incubator.d/manjaro.js +1 -0
  26. package/dist/classes/incubation/incubator.js +1 -1
  27. package/dist/classes/ovary.d/create-xdg-autostart.js +1 -1
  28. package/dist/classes/ovary.d/edit-live-fs.d.ts +2 -13
  29. package/dist/classes/ovary.d/edit-live-fs.js +33 -146
  30. package/dist/classes/ovary.d/fertilization.js +1 -1
  31. package/dist/classes/ovary.d/luks-home.js +33 -19
  32. package/dist/classes/ovary.d/luks-root.d.ts +1 -2
  33. package/dist/classes/ovary.d/luks-root.js +46 -27
  34. package/dist/classes/ovary.d/luks-shrink.d.ts +14 -0
  35. package/dist/classes/ovary.d/luks-shrink.js +86 -0
  36. package/dist/classes/ovary.d/make-dot-disk.js +1 -1
  37. package/dist/classes/ovary.d/produce.js +64 -22
  38. package/dist/classes/ovary.d/user-create-live.d.ts +4 -10
  39. package/dist/classes/ovary.d/user-create-live.js +82 -84
  40. package/dist/classes/ovary.d/users-remove.d.ts +5 -6
  41. package/dist/classes/ovary.d/users-remove.js +61 -31
  42. package/dist/classes/ovary.d.ts +5 -3
  43. package/dist/classes/ovary.js +5 -3
  44. package/dist/classes/pacman.d/alpine.js +2 -2
  45. package/dist/classes/pacman.d/archlinux.js +2 -2
  46. package/dist/classes/pacman.d/debian.js +2 -3
  47. package/dist/classes/pacman.d/fedora.js +2 -3
  48. package/dist/classes/pacman.d/openmamba.js +2 -3
  49. package/dist/classes/pacman.d/opensuse.js +2 -3
  50. package/dist/classes/pacman.d.ts +0 -5
  51. package/dist/classes/pacman.js +3 -16
  52. package/dist/classes/pve-live.js +1 -1
  53. package/dist/classes/settings.js +1 -1
  54. package/dist/classes/sys-users.d.ts +76 -0
  55. package/dist/classes/sys-users.js +206 -0
  56. package/dist/classes/utils.d/kernel.js +3 -3
  57. package/dist/classes/utils.d.ts +15 -6
  58. package/dist/classes/utils.js +80 -47
  59. package/dist/classes/xdg.js +1 -1
  60. package/dist/classes/yolk.js +3 -5
  61. package/dist/commands/export/appimage.js +3 -3
  62. package/dist/commands/export/pkg.js +3 -3
  63. package/dist/commands/export/tarballs.js +3 -3
  64. package/dist/commands/krill.js +1 -1
  65. package/dist/commands/produce.js +14 -5
  66. package/dist/commands/setup/install.js +1 -1
  67. package/dist/commands/setup/purge.js +1 -1
  68. package/dist/commands/tools/yolk.js +1 -1
  69. package/dist/commands/update.js +1 -2
  70. package/dist/interfaces/calamares/i-calamares-branding.d.ts +56 -38
  71. package/dist/interfaces/calamares/i-calamares-branding.js +10 -0
  72. package/dist/interfaces/i-exec.d.ts +1 -0
  73. package/dist/krill/classes/prepare.d/location.js +1 -1
  74. package/dist/krill/classes/prepare.d/partitions.js +1 -1
  75. package/dist/krill/classes/prepare.d/users.js +2 -2
  76. package/dist/krill/classes/prepare.js +5 -5
  77. package/dist/krill/classes/sequence.d/add_user.d.ts +3 -15
  78. package/dist/krill/classes/sequence.d/add_user.js +87 -57
  79. package/dist/krill/classes/sequence.d/change_password.d.ts +5 -7
  80. package/dist/krill/classes/sequence.d/change_password.js +25 -10
  81. package/dist/krill/classes/sequence.d/del_live_user.d.ts +5 -7
  82. package/dist/krill/classes/sequence.d/del_live_user.js +39 -25
  83. package/dist/krill/classes/sequence.d/fstab.js +2 -2
  84. package/dist/krill/classes/sequence.d/grubcfg.d.ts +3 -7
  85. package/dist/krill/classes/sequence.d/grubcfg.js +33 -13
  86. package/dist/krill/classes/sequence.d/mkfs.js +2 -3
  87. package/dist/krill/classes/sequence.d/unpackfs.d.ts +2 -4
  88. package/dist/krill/classes/sequence.d/unpackfs.js +8 -5
  89. package/dist/krill/classes/sequence.d.ts +1 -5
  90. package/dist/krill/classes/sequence.js +28 -32
  91. package/dist/krill/components/finished.js +2 -2
  92. package/dist/krill/components/install.js +2 -2
  93. package/dist/krill/components/keyboard.js +2 -2
  94. package/dist/krill/components/location.js +2 -2
  95. package/dist/krill/components/network.js +2 -2
  96. package/dist/krill/components/partitions.js +2 -2
  97. package/dist/krill/components/summary.js +2 -2
  98. package/dist/krill/components/title.js +2 -2
  99. package/dist/krill/components/users.js +2 -2
  100. package/dist/krill/components/welcome.js +2 -2
  101. package/dist/krill/lib/select_installation_device.js +1 -1
  102. package/dist/krill/lib/select_replaced_partition.js +1 -1
  103. package/dist/lib/utils.d.ts +52 -19
  104. package/dist/lib/utils.js +271 -20
  105. package/manpages/doc/man/eggs.1.gz +0 -0
  106. package/manpages/doc/man/eggs.html +8 -8
  107. package/package.json +9 -9
  108. package/perrisbrewery/template/dependencies.yaml +1 -0
  109. package/scripts/boot-encrypted-root.sh +220 -0
  110. package/scripts/mount-encrypted-home.sh +324 -0
  111. package/scripts/restore_homecrypt_krill.sh +93 -0
  112. package/dracut/create-symlink +0 -71
  113. package/dracut/dracut-log.txt +0 -3
  114. package/dracut/export +0 -4
  115. package/dracut/export-dracut-analysis +0 -51
  116. package/dracut/export-dracut-log +0 -2
  117. package/dracut/mkisofs +0 -10
  118. package/dracut/renew-initramfs +0 -17
  119. package/dracut/sbin2bin +0 -10
  120. package/dracut/update-dracut-conf-d +0 -2
  121. package/dracut/update-dracut-modules +0 -62
@@ -0,0 +1,76 @@
1
+ /**
2
+ * src/classes/sys-users.ts
3
+ * penguins-eggs v.25.7.x / ecmascript 2020
4
+ * * "THE SYSUSER MASTER"
5
+ * Gestione pura Node.js per utenti e gruppi di sistema.
6
+ * Sostituisce i binari (useradd/usermod/deluser) per garantire operazioni atomiche
7
+ * e compatibilità SELinux (Fedora/RHEL) scrivendo file puliti.
8
+ */
9
+ export interface IPasswdEntry {
10
+ username: string;
11
+ password: string;
12
+ uid: string;
13
+ gid: string;
14
+ gecos: string;
15
+ home: string;
16
+ shell: string;
17
+ }
18
+ export interface IShadowEntry {
19
+ username: string;
20
+ hash: string;
21
+ lastChange: string;
22
+ min: string;
23
+ max: string;
24
+ warn: string;
25
+ inactive: string;
26
+ expire: string;
27
+ }
28
+ export interface IGroupEntry {
29
+ groupName: string;
30
+ password: string;
31
+ gid: string;
32
+ members: string[];
33
+ }
34
+ export default class SysUsers {
35
+ private targetRoot;
36
+ private distroFamily;
37
+ private passwd;
38
+ private shadow;
39
+ private group;
40
+ private gshadowLines;
41
+ private subuidLines;
42
+ private subgidLines;
43
+ constructor(targetRoot: string, distroFamily: string);
44
+ /**
45
+ * Carica tutti i file di configurazione in memoria
46
+ */
47
+ load(): void;
48
+ /**
49
+ * Salva lo stato della memoria su disco e applica SELinux fix
50
+ */
51
+ save(): Promise<void>;
52
+ /**
53
+ * Crea un nuovo utente completo
54
+ */
55
+ addUser(user: IPasswdEntry, cleanPassword: string): void;
56
+ /**
57
+ * Rimuove completamente un utente
58
+ */
59
+ removeUser(username: string): void;
60
+ /**
61
+ * Aggiunge utente a un gruppo supplementare
62
+ */
63
+ addUserToGroup(username: string, groupName: string): void;
64
+ /**
65
+ * Cambia password utente
66
+ */
67
+ setPassword(username: string, password: string): void;
68
+ private readFile;
69
+ private writeFile;
70
+ private parsePasswd;
71
+ private serializePasswd;
72
+ private parseShadow;
73
+ private serializeShadow;
74
+ private parseGroup;
75
+ private serializeGroup;
76
+ }
@@ -0,0 +1,206 @@
1
+ /**
2
+ * src/classes/sys-users.ts
3
+ * penguins-eggs v.25.7.x / ecmascript 2020
4
+ * * "THE SYSUSER MASTER"
5
+ * Gestione pura Node.js per utenti e gruppi di sistema.
6
+ * Sostituisce i binari (useradd/usermod/deluser) per garantire operazioni atomiche
7
+ * e compatibilità SELinux (Fedora/RHEL) scrivendo file puliti.
8
+ */
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+ import * as bcrypt from 'bcryptjs';
12
+ import { exec } from '../lib/utils.js';
13
+ export default class SysUsers {
14
+ targetRoot;
15
+ distroFamily;
16
+ // Cache in memoria
17
+ passwd = [];
18
+ shadow = [];
19
+ group = [];
20
+ // File "minori" gestiti a righe raw per semplicità
21
+ gshadowLines = [];
22
+ subuidLines = [];
23
+ subgidLines = [];
24
+ constructor(targetRoot, distroFamily) {
25
+ this.targetRoot = targetRoot;
26
+ this.distroFamily = distroFamily;
27
+ }
28
+ /**
29
+ * Carica tutti i file di configurazione in memoria
30
+ */
31
+ load() {
32
+ this.passwd = this.parsePasswd(this.readFile('etc/passwd'));
33
+ this.shadow = this.parseShadow(this.readFile('etc/shadow'));
34
+ this.group = this.parseGroup(this.readFile('etc/group'));
35
+ this.gshadowLines = this.readFile('etc/gshadow');
36
+ this.subuidLines = this.readFile('etc/subuid');
37
+ this.subgidLines = this.readFile('etc/subgid');
38
+ }
39
+ /**
40
+ * Salva lo stato della memoria su disco e applica SELinux fix
41
+ */
42
+ async save() {
43
+ // Serializzazione
44
+ const passwdContent = this.serializePasswd(this.passwd);
45
+ const shadowContent = this.serializeShadow(this.shadow);
46
+ const groupContent = this.serializeGroup(this.group);
47
+ // Scrittura Atomica + Fix SELinux
48
+ await this.writeFile('etc/passwd', passwdContent, 'passwd_file_t');
49
+ await this.writeFile('etc/shadow', shadowContent, 'shadow_t');
50
+ await this.writeFile('etc/group', groupContent, 'passwd_file_t');
51
+ // File raw
52
+ if (this.gshadowLines.length > 0)
53
+ await this.writeFile('etc/gshadow', this.gshadowLines.join('\n'), 'shadow_t');
54
+ if (this.subuidLines.length > 0)
55
+ await this.writeFile('etc/subuid', this.subuidLines.join('\n'), 'passwd_file_t');
56
+ if (this.subgidLines.length > 0)
57
+ await this.writeFile('etc/subgid', this.subgidLines.join('\n'), 'passwd_file_t');
58
+ }
59
+ // =========================================================================
60
+ // API PUBBLICA
61
+ // =========================================================================
62
+ /**
63
+ * Crea un nuovo utente completo
64
+ */
65
+ addUser(user, cleanPassword) {
66
+ // Rimuovi se esiste (idempotenza)
67
+ this.removeUser(user.username);
68
+ // 1. Passwd
69
+ this.passwd.push(user);
70
+ // 2. Shadow (Hash Password)
71
+ const salt = bcrypt.genSaltSync(10);
72
+ const hash = bcrypt.hashSync(cleanPassword, salt);
73
+ this.shadow.push({
74
+ username: user.username,
75
+ hash: hash,
76
+ lastChange: '19700', // Data approssimativa
77
+ min: '0',
78
+ max: '99999',
79
+ warn: '7',
80
+ inactive: '',
81
+ expire: ''
82
+ });
83
+ // 3. Gruppo Primario
84
+ // Solo se non esiste già un gruppo con quel nome
85
+ if (!this.group.find(g => g.groupName === user.username)) {
86
+ this.group.push({
87
+ groupName: user.username,
88
+ password: 'x',
89
+ gid: user.gid,
90
+ members: []
91
+ });
92
+ }
93
+ // 4. GShadow (placeholder)
94
+ this.gshadowLines.push(`${user.username}:!::`);
95
+ // 5. SubUID/SubGID (Podman rootless)
96
+ // Calcolo offset standard: 100000 + (UID-1000)*65536
97
+ const uidNum = parseInt(user.uid);
98
+ if (!isNaN(uidNum) && uidNum >= 1000) {
99
+ const startUid = 100000 + (uidNum - 1000) * 65536;
100
+ const subEntry = `${user.username}:${startUid}:65536`;
101
+ this.subuidLines.push(subEntry);
102
+ this.subgidLines.push(subEntry);
103
+ }
104
+ }
105
+ /**
106
+ * Rimuove completamente un utente
107
+ */
108
+ removeUser(username) {
109
+ this.passwd = this.passwd.filter(u => u.username !== username);
110
+ this.shadow = this.shadow.filter(s => s.username !== username);
111
+ this.group = this.group.filter(g => g.groupName !== username);
112
+ // Rimuovi dai membri di altri gruppi
113
+ this.group.forEach(g => {
114
+ g.members = g.members.filter(m => m !== username);
115
+ });
116
+ this.gshadowLines = this.gshadowLines.filter(l => !l.startsWith(`${username}:`));
117
+ this.subuidLines = this.subuidLines.filter(l => !l.startsWith(`${username}:`));
118
+ this.subgidLines = this.subgidLines.filter(l => !l.startsWith(`${username}:`));
119
+ }
120
+ /**
121
+ * Aggiunge utente a un gruppo supplementare
122
+ */
123
+ addUserToGroup(username, groupName) {
124
+ const grp = this.group.find(g => g.groupName === groupName);
125
+ if (grp) {
126
+ if (!grp.members.includes(username)) {
127
+ grp.members.push(username);
128
+ }
129
+ }
130
+ // Se il gruppo non esiste, lo ignoriamo silenziosamente o potremmo crearlo
131
+ }
132
+ /**
133
+ * Cambia password utente
134
+ */
135
+ setPassword(username, password) {
136
+ const entry = this.shadow.find(s => s.username === username);
137
+ if (entry) {
138
+ const salt = bcrypt.genSaltSync(10);
139
+ entry.hash = bcrypt.hashSync(password, salt);
140
+ entry.lastChange = '19700';
141
+ }
142
+ }
143
+ // =========================================================================
144
+ // IMPLEMENTAZIONE FILE (Privata)
145
+ // =========================================================================
146
+ readFile(relativePath) {
147
+ const fullPath = path.join(this.targetRoot, relativePath);
148
+ if (fs.existsSync(fullPath)) {
149
+ return fs.readFileSync(fullPath, 'utf8').split('\n').filter(l => l.trim().length > 0);
150
+ }
151
+ return [];
152
+ }
153
+ async writeFile(relativePath, content, contextType) {
154
+ const fullPath = path.join(this.targetRoot, relativePath);
155
+ // Crea dir se manca (es. /etc/sudoers.d/ o simili)
156
+ const dir = path.dirname(fullPath);
157
+ if (!fs.existsSync(dir))
158
+ fs.mkdirSync(dir, { recursive: true });
159
+ try {
160
+ // 1. Scrittura
161
+ fs.writeFileSync(fullPath, content + '\n');
162
+ // 2. Fix SELinux (Solo RHEL Family)
163
+ if (['fedora', 'rhel', 'centos', 'almalinux', 'rocky'].includes(this.distroFamily)) {
164
+ // await exec, echo false per non sporcare i log
165
+ await exec(`chcon -t ${contextType} ${fullPath}`, { echo: false }).catch(() => { });
166
+ }
167
+ }
168
+ catch (e) {
169
+ console.error(`SysUsers Error writing ${relativePath}:`, e);
170
+ }
171
+ }
172
+ // --- PARSERS & SERIALIZERS ---
173
+ parsePasswd(lines) {
174
+ return lines.map(line => {
175
+ const p = line.split(':');
176
+ if (p.length < 7)
177
+ return null;
178
+ return { username: p[0], password: p[1], uid: p[2], gid: p[3], gecos: p[4], home: p[5], shell: p[6] };
179
+ }).filter((u) => u !== null);
180
+ }
181
+ serializePasswd(entries) {
182
+ return entries.map(u => `${u.username}:${u.password}:${u.uid}:${u.gid}:${u.gecos}:${u.home}:${u.shell}`).join('\n');
183
+ }
184
+ parseShadow(lines) {
185
+ return lines.map(line => {
186
+ const p = line.split(':');
187
+ if (p.length < 2)
188
+ return null;
189
+ return { username: p[0], hash: p[1], lastChange: p[2] || '', min: p[3] || '', max: p[4] || '', warn: p[5] || '', inactive: p[6] || '', expire: p[7] || '' };
190
+ }).filter((u) => u !== null);
191
+ }
192
+ serializeShadow(entries) {
193
+ return entries.map(s => `${s.username}:${s.hash}:${s.lastChange}:${s.min}:${s.max}:${s.warn}:${s.inactive}:${s.expire}:`).join('\n');
194
+ }
195
+ parseGroup(lines) {
196
+ return lines.map(line => {
197
+ const p = line.split(':');
198
+ if (p.length < 3)
199
+ return null;
200
+ return { groupName: p[0], password: p[1], gid: p[2], members: p[3] && p[3].trim() ? p[3].split(',') : [] };
201
+ }).filter((g) => g !== null);
202
+ }
203
+ serializeGroup(entries) {
204
+ return entries.map(g => `${g.groupName}:${g.password}:${g.gid}:${g.members.join(',')}`).join('\n');
205
+ }
206
+ }
@@ -8,7 +8,7 @@ import fs from 'node:fs';
8
8
  import path from 'path';
9
9
  import Distro from '../distro.js';
10
10
  import Utils from '../utils.js';
11
- import { execSync } from 'node:child_process';
11
+ import { execSync } from '../../lib/utils.js';
12
12
  /**
13
13
  * Kernel utilities for managing vmlinuz and initramfs paths
14
14
  */
@@ -55,7 +55,7 @@ export default class Kernel {
55
55
  Utils.warning("Non è possibile determinare il kernel in un container.");
56
56
  process.exit(1);
57
57
  }
58
- targetKernel = execSync('uname -r').toString().trim();
58
+ targetKernel = (execSync('uname -r', { stdio: 'ignore' }) || '').trim();
59
59
  }
60
60
  const kernelVersionShort = targetKernel.split('.').slice(0, 2).join('.');
61
61
  const bootDir = '/boot';
@@ -138,7 +138,7 @@ export default class Kernel {
138
138
  * debian, fedora, opensuse, rasberry
139
139
  */
140
140
  static vmlinuzFromUname() {
141
- let kernelVersion = execSync('uname -r').toString().trim();
141
+ let kernelVersion = (execSync('uname -r', { stdio: 'ignore' }) || '').trim();
142
142
  // Try 1: path standard (es. Debian, Ubuntu, Fedora)
143
143
  let standardPath = `/boot/vmlinuz-${kernelVersion}`;
144
144
  if (fs.existsSync(standardPath)) {
@@ -62,7 +62,7 @@ export default class Utils {
62
62
  */
63
63
  static isOpenRc(): boolean;
64
64
  /**
65
- * Occore vedere un modo per creare machine-id dove non esiste
65
+ * Usata da pacman e config credo non serva affatto
66
66
  */
67
67
  static machineId(): string;
68
68
  /**
@@ -153,11 +153,10 @@ export default class Utils {
153
153
  */
154
154
  static getUsedSpace(): number;
155
155
  /**
156
- * Extimate the linuxfs dimension
157
- * probably is better to rename it as
158
- * getLiveSpaceRootNeed
159
- * @returns {number} Byte
160
- */
156
+ * Estimate the linuxfs dimension
157
+ * (Refactored to use native FS instead of dd/od)
158
+ * @returns {number} GB
159
+ */
161
160
  static getLiveRootSpace(type?: string): number;
162
161
  /**
163
162
  * Return true if i686 architecture
@@ -327,4 +326,14 @@ export default class Utils {
327
326
  static wardrobe(): Promise<string>;
328
327
  static getOsRelease(): IOsRelease;
329
328
  static sleep(ms?: number): Promise<void>;
329
+ /**
330
+ * chpasswdPath
331
+ * @returns
332
+ */
333
+ static chpasswdPath(): string;
334
+ /**
335
+ *
336
+ * @param cmd
337
+ */
338
+ static commandExists(cmd: string): boolean;
330
339
  }
@@ -7,13 +7,12 @@
7
7
  *
8
8
  * Refactored Utils class - imports from modular utilities
9
9
  */
10
- import shx from 'shelljs';
10
+ import { shx, spawnSync } from '../lib/utils.js';
11
11
  import fs from 'fs';
12
12
  import dns from 'dns';
13
13
  import path from 'path';
14
14
  import os from 'os';
15
15
  import inquirer from 'inquirer';
16
- import { execSync, spawnSync } from 'child_process';
17
16
  import chalk from 'chalk';
18
17
  import Kernel from './utils.d/kernel.js';
19
18
  // libraries
@@ -155,18 +154,12 @@ export default class Utils {
155
154
  static isOpenRc() {
156
155
  let isOpenRc = false;
157
156
  if (!this.isContainer()) {
158
- try {
159
- execSync('command -v openrc');
160
- isOpenRc = true;
161
- }
162
- catch (error) {
163
- isOpenRc = false;
164
- }
157
+ isOpenRc = Utils.commandExists('openrc');
165
158
  }
166
159
  return isOpenRc;
167
160
  }
168
161
  /**
169
- * Occore vedere un modo per creare machine-id dove non esiste
162
+ * Usata da pacman e config credo non serva affatto
170
163
  */
171
164
  static machineId() {
172
165
  let result = '';
@@ -245,7 +238,7 @@ export default class Utils {
245
238
  * @param device
246
239
  */
247
240
  static uuid(device) {
248
- const uuid = shx.exec(`blkid -s UUID -o value ${device}`).stdout.trim();
241
+ const uuid = shx.exec(`blkid -p -s UUID -o value ${device}`, { silent: true }).stdout.trim();
249
242
  return uuid;
250
243
  }
251
244
  /**
@@ -436,49 +429,71 @@ export default class Utils {
436
429
  return fileSizeInBytes;
437
430
  }
438
431
  /**
439
- * Extimate the linuxfs dimension
440
- * probably is better to rename it as
441
- * getLiveSpaceRootNeed
442
- * @returns {number} Byte
443
- */
432
+ * Estimate the linuxfs dimension
433
+ * (Refactored to use native FS instead of dd/od)
434
+ * @returns {number} GB
435
+ */
444
436
  static getLiveRootSpace(type = 'debian-live') {
445
437
  let squashFs = '/run/live/medium/live/filesystem.squashfs';
446
438
  if (type === 'mx') {
447
439
  squashFs = '/live/boot-dev/antiX/linuxfs';
448
440
  }
449
- // Ottengo la dimensione del file compresso
450
- const compressedFs = fs.statSync(squashFs).size;
451
- // get compression factor by reading the linuxfs squasfs file, if available
452
- const compressedFs_compression_type = shx.exec(`dd if=${compressedFs} bs=1 skip=20 count=2 status=none 2>/dev/null| /usr/bin/od -An -tdI`);
453
- let compression_factor = 0;
454
- if (compressedFs_compression_type === '1') {
455
- compression_factor = 37; // gzip
456
- }
457
- else if (compressedFs_compression_type === '2') {
458
- compression_factor = 52; // lzo, not used by antiX
459
- }
460
- else if (compressedFs_compression_type === '3') {
461
- compression_factor = 52; // lzma, not used by antiX
462
- }
463
- else if (compressedFs_compression_type === '4') {
464
- compression_factor = 31; // xz
465
- }
466
- else if (compressedFs_compression_type === '5') {
467
- compression_factor = 52; // lz4
468
- }
469
- else {
470
- compression_factor = 30; // anything else or linuxfs not reachable (toram), should be pretty conservative
441
+ // 1. Leggiamo il tipo di compressione DIRETTAMENTE dall'header del file SquashFS
442
+ // L'identificativo della compressione è a offset 20 (2 bytes, little endian)
443
+ // 1=gzip, 2=lzo, 3=lzma, 4=xz, 5=lz4, 6=zstd
444
+ let compressionId = 0;
445
+ try {
446
+ if (fs.existsSync(squashFs)) {
447
+ const fd = fs.openSync(squashFs, 'r');
448
+ const buffer = Buffer.alloc(2);
449
+ // Leggi 2 byte alla posizione 20
450
+ fs.readSync(fd, buffer, 0, 2, 20);
451
+ fs.closeSync(fd);
452
+ compressionId = buffer.readUInt16LE(0);
453
+ }
471
454
  }
455
+ catch (e) {
456
+ console.error("Error reading squashfs header:", e);
457
+ }
458
+ // 2. Determiniamo il fattore di compressione in base all'ID letto
459
+ let compression_factor = 30; // Default conservative
460
+ switch (compressionId) {
461
+ case 1: // gzip
462
+ compression_factor = 37;
463
+ break;
464
+ case 2: // lzo
465
+ compression_factor = 52;
466
+ break;
467
+ case 3: // lzma
468
+ compression_factor = 52;
469
+ break;
470
+ case 4: // xz
471
+ compression_factor = 31;
472
+ break;
473
+ case 5: // lz4
474
+ compression_factor = 52;
475
+ break;
476
+ case 6: // zstd (aggiunto per completezza)
477
+ compression_factor = 37; // simile a gzip come ratio medio
478
+ break;
479
+ default:
480
+ compression_factor = 30;
481
+ }
482
+ // 3. Calcolo dimensione Linux FS
483
+ // Nota: shx.exec ritorna un oggetto, dobbiamo prendere .stdout e pulirlo
472
484
  let rootfs_file_size = 0;
473
- const linuxfs_file_size = (Number(shx.exec('df /live/linux --output=used --total | /usr/bin/tail -n1').stdout.trim()) * 1024 * 100) / compression_factor;
485
+ const dfCmdLinux = 'df /live/linux --output=used --total | /usr/bin/tail -n1';
486
+ const dfResultLinux = shx.exec(dfCmdLinux, { silent: true }).stdout.trim();
487
+ const linuxfs_used = Number(dfResultLinux) || 0; // Gestione caso NaN
488
+ const linuxfs_file_size = (linuxfs_used * 1024 * 100) / compression_factor;
489
+ // 4. Calcolo persist-root (se esiste)
474
490
  if (fs.existsSync('/live/persist-root')) {
475
- rootfs_file_size = Number(shx.exec('df /live/persist-root --output=used --total | /usr/bin/tail -n1').stdout.trim()) * 1024;
491
+ const dfCmdRoot = 'df /live/persist-root --output=used --total | /usr/bin/tail -n1';
492
+ const dfResultRoot = shx.exec(dfCmdRoot, { silent: true }).stdout.trim();
493
+ rootfs_file_size = (Number(dfResultRoot) || 0) * 1024;
476
494
  }
477
495
  let rootSpaceNeeded;
478
496
  if (type === 'mx') {
479
- /**
480
- * add rootfs file size to the calculated linuxfs file size. Probaby conservative, as rootfs will likely have some overlap with linuxfs
481
- */
482
497
  rootSpaceNeeded = linuxfs_file_size + rootfs_file_size;
483
498
  }
484
499
  else {
@@ -845,7 +860,7 @@ export default class Utils {
845
860
  msg = 'Press a key to continue...';
846
861
  }
847
862
  console.log(msg);
848
- const pressKeyToExit = spawnSync('read _ ', { shell: true, stdio: [0, 1, 2] });
863
+ const pressKeyToExit = spawnSync('read _ ', [], { shell: true, stdio: [0, 1, 2] });
849
864
  if (!procContinue) {
850
865
  process.exit(0);
851
866
  }
@@ -857,7 +872,7 @@ export default class Utils {
857
872
  msg = 'Press a key to continue...';
858
873
  }
859
874
  console.log(msg);
860
- const pressKeyToExit = spawnSync('read _ ', { shell: true, stdio: [0, 1, 2] });
875
+ const pressKeyToExit = spawnSync('read _ ', [], { shell: true, stdio: [0, 1, 2] });
861
876
  if (!procContinue) {
862
877
  process.exit(0);
863
878
  }
@@ -881,12 +896,12 @@ export default class Utils {
881
896
  static flag() {
882
897
  let type = '';
883
898
  if (!Utils.isAppImage()) {
884
- type = 'native';
899
+ type = ' native';
885
900
  }
886
901
  let title = `${pjson.name}`;
887
902
  let green = ` ${title}`.padEnd(25, " ");
888
903
  let white = ` Perri's brewery edition `.padEnd(25, " ");
889
- let red = ` v${pjson.version} ${type} `.padStart(25, " ");
904
+ let red = ` v${pjson.version}${type} `.padStart(25, " ");
890
905
  return chalk.bgGreen.whiteBright(green) +
891
906
  chalk.bgWhite.blue(white) +
892
907
  chalk.bgRed.whiteBright(red);
@@ -984,4 +999,22 @@ export default class Utils {
984
999
  // console.log('wait...')
985
1000
  return new Promise(resolve => setTimeout(resolve, ms));
986
1001
  }
1002
+ /**
1003
+ * chpasswdPath
1004
+ * @returns
1005
+ */
1006
+ static chpasswdPath() {
1007
+ let chpasswdPath = '/usr/sbin/chpasswd';
1008
+ if (fs.existsSync(chpasswdPath)) {
1009
+ chpasswdPath = '/usr/bin/chpasswd';
1010
+ }
1011
+ return chpasswdPath;
1012
+ }
1013
+ /**
1014
+ *
1015
+ * @param cmd
1016
+ */
1017
+ static commandExists(cmd) {
1018
+ return !!shx.which(cmd);
1019
+ }
987
1020
  }
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import fs from 'node:fs';
9
9
  import path from 'node:path';
10
- import shx from 'shelljs';
10
+ import { shx } from '../lib/utils.js';
11
11
  // libraries
12
12
  import { exec } from '../lib/utils.js';
13
13
  import Distro from './distro.js';
@@ -7,10 +7,8 @@
7
7
  */
8
8
  import yaml from 'js-yaml';
9
9
  import fs from 'node:fs';
10
- import shx from 'shelljs';
11
- import { exec } from '../lib/utils.js';
10
+ import { shx, exec } from '../lib/utils.js';
12
11
  import Bleach from './bleach.js';
13
- import Pacman from './pacman.js';
14
12
  import Utils from './utils.js';
15
13
  /**
16
14
  *
@@ -31,7 +29,7 @@ export default class Yolk {
31
29
  }
32
30
  Utils.warning(`Creating yolk on ${this.yolkDir}`);
33
31
  Utils.warning('Updating system');
34
- if (!Pacman.commandIsInstalled('dpkg-scanpackages')) {
32
+ if (!Utils.commandExists('dpkg-scanpackages')) {
35
33
  Utils.warning(`I cannot find the command dpkg-scanpackages`);
36
34
  process.exit(0);
37
35
  }
@@ -68,7 +66,7 @@ export default class Yolk {
68
66
  Utils.warning(cmd);
69
67
  await exec(cmd, { capture: true, echo: false });
70
68
  // Create Release date: Sat, 14 Aug 2021 07:42:00 UTC
71
- const now = shx.exec('date -R -u').stdout.trim();
69
+ const now = shx.exec('date -R -u', { silent: true }).stdout.trim();
72
70
  const content = `Archive: stable\nComponent: yolk\nOrigin: penguins-eggs\nArchitecture: ${Utils.uefiArch()} \nDate: ${now}\n`;
73
71
  Utils.warning('Writing Release');
74
72
  fs.writeFileSync('Release', content);
@@ -10,7 +10,7 @@ import Tools from '../../classes/tools.js';
10
10
  import Utils from '../../classes/utils.js';
11
11
  import { exec } from '../../lib/utils.js';
12
12
  import os from 'node:os';
13
- import { execSync } from 'node:child_process';
13
+ import { execSync } from '../../lib/utils.js';
14
14
  export default class ExportAppimage extends Command {
15
15
  static description = 'export penguins-eggs AppImage to the destination host';
16
16
  static examples = ['eggs export pkg', 'eggs export pkg --clean', 'eggs export pkg --all'];
@@ -34,9 +34,9 @@ export default class ExportAppimage extends Command {
34
34
  // Ora servono in più parti
35
35
  this.user = os.userInfo().username;
36
36
  if (this.user === 'root') {
37
- this.user = execSync('echo $SUDO_USER', { encoding: 'utf-8' }).trim();
37
+ this.user = (execSync('echo $DOAS_USER') || '').trim();
38
38
  if (this.user === '') {
39
- this.user = execSync('echo $DOAS_USER', { encoding: 'utf-8' }).trim();
39
+ this.user = (execSync('echo $DOAS_USER') || '').trim();
40
40
  }
41
41
  }
42
42
  this.clean = flags.clean;
@@ -12,7 +12,7 @@ import Tools from '../../classes/tools.js';
12
12
  import Utils from '../../classes/utils.js';
13
13
  import { exec } from '../../lib/utils.js';
14
14
  import os from 'node:os';
15
- import { execSync } from 'node:child_process';
15
+ import { execSync } from '../../lib/utils.js';
16
16
  export default class ExportPkg extends Command {
17
17
  static description = 'export penguins-eggs package to the destination host';
18
18
  static examples = ['eggs export pkg', 'eggs export pkg --clean', 'eggs export pkg --all'];
@@ -38,9 +38,9 @@ export default class ExportPkg extends Command {
38
38
  // Ora servono in più parti
39
39
  this.user = os.userInfo().username;
40
40
  if (this.user === 'root') {
41
- this.user = execSync('echo $SUDO_USER', { encoding: 'utf-8' }).trim();
41
+ this.user = (execSync('echo $DOAS_USER') || '').trim();
42
42
  if (this.user === '') {
43
- this.user = execSync('echo $DOAS_USER', { encoding: 'utf-8' }).trim();
43
+ this.user = (execSync('echo $DOAS_USER') || '').trim();
44
44
  }
45
45
  }
46
46
  this.all = flags.all;
@@ -16,7 +16,7 @@ import path from 'path';
16
16
  import { createRequire } from 'module';
17
17
  const require = createRequire(import.meta.url);
18
18
  const pjson = require('../../../package.json');
19
- import { execSync } from 'node:child_process';
19
+ import { execSync } from '../../lib/utils.js';
20
20
  export default class ExportTarballs extends Command {
21
21
  static description = 'export pkg/iso/tarballs to the destination host';
22
22
  static examples = ['eggs export tarballs', 'eggs export tarballs --clean'];
@@ -40,9 +40,9 @@ export default class ExportTarballs extends Command {
40
40
  // Ora servono in più parti
41
41
  this.user = os.userInfo().username;
42
42
  if (this.user === 'root') {
43
- this.user = execSync('echo $SUDO_USER', { encoding: 'utf-8' }).trim();
43
+ this.user = (execSync('echo $DOAS_USER') || '').trim();
44
44
  if (this.user === '') {
45
- this.user = execSync('echo $DOAS_USER', { encoding: 'utf-8' }).trim();
45
+ this.user = (execSync('echo $DOAS_USER') || '').trim();
46
46
  }
47
47
  }
48
48
  this.clean = flags.clean;
@@ -9,7 +9,7 @@ import { Command, Flags } from '@oclif/core';
9
9
  import yaml from 'js-yaml';
10
10
  import fs from 'node:fs';
11
11
  import https from 'node:https';
12
- import shx from 'shelljs';
12
+ import { shx } from '../lib/utils.js';
13
13
  import Utils from '../classes/utils.js';
14
14
  import Krill from '../krill/classes/prepare.js';
15
15
  const agent = new https.Agent({