pacman-debian 7.2.0 → 7.3.0
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.
- package/README.md +22 -2
- package/README_zh-CN.md +89 -19
- package/dist/cli/paclink.d.ts.map +1 -0
- package/dist/cli/paclink.js +261 -0
- package/dist/cli/paclink.js.map +1 -0
- package/dist/cli/pacman.d.ts.map +1 -1
- package/dist/cli/pacman.js +64 -38
- package/dist/cli/pacman.js.map +1 -1
- package/dist/core/compress.d.ts.map +1 -1
- package/dist/core/compress.js +29 -0
- package/dist/core/compress.js.map +1 -1
- package/dist/core/deps.d.ts.map +1 -1
- package/dist/core/deps.js +133 -68
- package/dist/core/deps.js.map +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/database.js +3 -1
- package/dist/db/database.js.map +1 -1
- package/dist/db/localdb.d.ts.map +1 -1
- package/dist/db/localdb.js +73 -4
- package/dist/db/localdb.js.map +1 -1
- package/dist/i18n/en.json +6 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +52 -6
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/paclink/en.d.ts.map +1 -0
- package/dist/i18n/paclink/en.js +62 -0
- package/dist/i18n/paclink/en.js.map +1 -0
- package/dist/i18n/paclink/zh-CN.d.ts.map +1 -0
- package/dist/i18n/paclink/zh-CN.js +62 -0
- package/dist/i18n/paclink/zh-CN.js.map +1 -0
- package/dist/i18n/setup/en.d.ts.map +1 -0
- package/dist/i18n/setup/en.js +49 -0
- package/dist/i18n/setup/en.js.map +1 -0
- package/dist/i18n/setup/zh-CN.d.ts.map +1 -0
- package/dist/i18n/setup/zh-CN.js +49 -0
- package/dist/i18n/setup/zh-CN.js.map +1 -0
- package/dist/i18n/zh-CN.json +6 -0
- package/dist/ops/install.d.ts.map +1 -1
- package/dist/ops/install.js +238 -45
- package/dist/ops/install.js.map +1 -1
- package/dist/ops/query.d.ts.map +1 -1
- package/dist/ops/query.js +25 -9
- package/dist/ops/query.js.map +1 -1
- package/dist/ops/remove.d.ts.map +1 -1
- package/dist/ops/remove.js +213 -42
- package/dist/ops/remove.js.map +1 -1
- package/dist/ops/upgrade.d.ts.map +1 -1
- package/dist/ops/upgrade.js +13 -15
- package/dist/ops/upgrade.js.map +1 -1
- package/dist/repo/repository.d.ts.map +1 -1
- package/dist/repo/repository.js +246 -90
- package/dist/repo/repository.js.map +1 -1
- package/dist/scripts/setup.js +119 -116
- package/dist/scripts/setup.js.map +1 -1
- package/package.json +3 -2
- package/README.zh.md +0 -388
- package/dist/ar.d.ts +0 -8
- package/dist/cli/pacman.d.ts +0 -2
- package/dist/compress.d.ts +0 -2
- package/dist/config.d.ts +0 -3
- package/dist/control.d.ts +0 -2
- package/dist/core/ar.d.ts +0 -7
- package/dist/core/compress.d.ts +0 -2
- package/dist/core/control.d.ts +0 -2
- package/dist/core/deb.d.ts +0 -4
- package/dist/core/deps.d.ts +0 -25
- package/dist/core/options.d.ts +0 -19
- package/dist/core/pkgfile.d.ts +0 -35
- package/dist/core/tar.d.ts +0 -13
- package/dist/core/types.d.ts +0 -83
- package/dist/database.d.ts +0 -20
- package/dist/db/database.d.ts +0 -17
- package/dist/db/dpkg-compat.d.ts +0 -19
- package/dist/db/localdb.d.ts +0 -9
- package/dist/db/sqlite.d.ts +0 -20
- package/dist/deb.d.ts +0 -5
- package/dist/dpkg-compat.d.ts +0 -18
- package/dist/i18n/index.d.ts +0 -3
- package/dist/index.d.ts +0 -3
- package/dist/install.d.ts +0 -2
- package/dist/makepkg/build.d.ts +0 -19
- package/dist/makepkg/index.d.ts +0 -3
- package/dist/makepkg/pkgbuild.d.ts +0 -36
- package/dist/ops/install.d.ts +0 -5
- package/dist/ops/query.d.ts +0 -9
- package/dist/ops/remove.d.ts +0 -3
- package/dist/ops/upgrade.d.ts +0 -4
- package/dist/pacman.d.ts +0 -2
- package/dist/query.d.ts +0 -5
- package/dist/remove.d.ts +0 -2
- package/dist/repo/config.d.ts +0 -3
- package/dist/repo/repository.d.ts +0 -10
- package/dist/repository.d.ts +0 -10
- package/dist/scripts/pacman-conf.d.ts +0 -3
- package/dist/scripts/setup.d.ts +0 -3
- package/dist/tar.d.ts +0 -17
- package/dist/types.d.ts +0 -80
- package/dist/ui/colors.d.ts +0 -13
- package/dist/ui/format.d.ts +0 -3
- package/dist/ui/progress.d.ts +0 -8
- package/dist/ui/prompt.d.ts +0 -4
- package/lib/pac4deb/Makefile +0 -26
- package/lib/pac4deb/README.md +0 -47
- package/lib/pac4deb/include/alpm.h +0 -166
- package/lib/pac4deb/include/alpm_list.h +0 -42
- package/lib/pac4deb/libalpm.so +0 -0
- package/lib/pac4deb/src/alpm_list.c +0 -102
- package/lib/pac4deb/src/genstubs.sh +0 -51
- package/lib/pac4deb/src/genstubs2.sh +0 -72
- package/lib/pac4deb/src/genstubs3.sh +0 -43
- package/lib/pac4deb/src/libalpm.c +0 -537
- package/lib/pac4deb/src/stubs_manual.c +0 -198
- package/lib/pac4deb/stubs.c +0 -6
- package/lib/pac4deb/update_header.sh +0 -15
- package/src/cli/pacman.ts +0 -308
- package/src/core/ar.ts +0 -54
- package/src/core/compress.ts +0 -27
- package/src/core/control.ts +0 -22
- package/src/core/deb.ts +0 -47
- package/src/core/deps.ts +0 -260
- package/src/core/options.ts +0 -20
- package/src/core/pkgfile.ts +0 -146
- package/src/core/tar.ts +0 -101
- package/src/core/types.ts +0 -89
- package/src/db/database.ts +0 -102
- package/src/db/dpkg-compat.ts +0 -129
- package/src/db/localdb.ts +0 -181
- package/src/i18n/en.json +0 -114
- package/src/i18n/index.ts +0 -32
- package/src/i18n/zh-CN.json +0 -114
- package/src/index.ts +0 -7
- package/src/makepkg/build.ts +0 -351
- package/src/makepkg/index.ts +0 -87
- package/src/makepkg/pkgbuild.ts +0 -146
- package/src/ops/install.ts +0 -260
- package/src/ops/query.ts +0 -117
- package/src/ops/remove.ts +0 -77
- package/src/ops/upgrade.ts +0 -87
- package/src/repo/config.ts +0 -96
- package/src/repo/repository.ts +0 -520
- package/src/scripts/pacman-conf.ts +0 -68
- package/src/scripts/setup.ts +0 -261
- package/src/ui/colors.ts +0 -40
- package/src/ui/format.ts +0 -9
- package/src/ui/progress.ts +0 -26
- package/src/ui/prompt.ts +0 -21
- package/tsconfig.json +0 -19
package/src/ops/install.ts
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { execSync } from 'node:child_process';
|
|
4
|
-
import { parseDeb, readScript } from '../core/deb';
|
|
5
|
-
import { extractTar } from '../core/tar';
|
|
6
|
-
import { parsePkgTarZst } from '../core/pkgfile';
|
|
7
|
-
import { findInRepo, downloadPkg } from '../repo/repository';
|
|
8
|
-
import {
|
|
9
|
-
initDb, loadDatabase, saveDatabase, addPackage, isInstalled, getPackage,
|
|
10
|
-
saveScript, runScript, createTransaction, completeTransaction, parseDepends,
|
|
11
|
-
} from '../db/database';
|
|
12
|
-
import { writeDpkgEntry, dpkgHasPackage } from '../db/dpkg-compat';
|
|
13
|
-
import { resolveDeps, detectConflicts } from '../core/deps';
|
|
14
|
-
import { formatBytes } from '../ui/format';
|
|
15
|
-
import { humanSize, drawProgressBar, formatRate, formatETA } from '../ui/progress';
|
|
16
|
-
import { confirm } from '../ui/prompt';
|
|
17
|
-
import { t } from '../i18n';
|
|
18
|
-
import type { InstalledPackage, RepoPkg } from '../core/types';
|
|
19
|
-
import type { InstallOptions } from '../core/options';
|
|
20
|
-
|
|
21
|
-
async function installDeb(filePath: string, reason: 'explicit' | 'dependency', opts: InstallOptions = {}): Promise<boolean> {
|
|
22
|
-
initDb();
|
|
23
|
-
const pkg = parseDeb(filePath);
|
|
24
|
-
const { control } = pkg;
|
|
25
|
-
const db = loadDatabase();
|
|
26
|
-
const existing = getPackage(db, control.package);
|
|
27
|
-
|
|
28
|
-
if (opts.needed && existing && existing.version === control.version) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
if (existing && existing.version === control.version) {
|
|
32
|
-
const realFiles = existing.files.filter(f => { try { return !fs.lstatSync(f).isDirectory(); } catch { return false; } });
|
|
33
|
-
if (realFiles.length > 0) return true;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const tx = createTransaction('install', control.package, control.version);
|
|
37
|
-
if (!opts.noscriptlet) {
|
|
38
|
-
const preinst = readScript(pkg, 'preinst');
|
|
39
|
-
if (preinst) saveScript(control.package, 'preinst', preinst);
|
|
40
|
-
runScript(control.package, 'preinst', ['install']);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const files = extractTar(pkg.dataTar, '/');
|
|
44
|
-
|
|
45
|
-
if (!opts.noscriptlet) {
|
|
46
|
-
const postinst = readScript(pkg, 'postinst');
|
|
47
|
-
if (postinst) saveScript(control.package, 'postinst', postinst);
|
|
48
|
-
runScript(control.package, 'postinst', ['configure']);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const ip: InstalledPackage = {
|
|
52
|
-
name: control.package, version: control.version,
|
|
53
|
-
architecture: control.architecture || 'amd64',
|
|
54
|
-
description: control.description || '',
|
|
55
|
-
depends: control.depends, 'pre-depends': control['pre-depends'],
|
|
56
|
-
conflicts: control.conflicts, provides: control.provides,
|
|
57
|
-
maintainer: control.maintainer, homepage: control.homepage,
|
|
58
|
-
controlSection: control.section || 'misc',
|
|
59
|
-
controlPriority: control.priority || 'optional',
|
|
60
|
-
installedSize: control['installed-size'] ? parseInt(control['installed-size'], 10) : undefined,
|
|
61
|
-
installTime: Date.now(), reason, files,
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
addPackage(db, ip);
|
|
65
|
-
try { writeDpkgEntry(ip); } catch (e) { console.error(' WARNING: failed to write dpkg status:', e); }
|
|
66
|
-
saveDatabase(db);
|
|
67
|
-
completeTransaction(tx.id);
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async function installArch(filePath: string, reason: 'explicit' | 'dependency', opts: InstallOptions = {}): Promise<boolean> {
|
|
72
|
-
initDb();
|
|
73
|
-
const data = fs.readFileSync(filePath);
|
|
74
|
-
const { info, install, files: pkgFiles, dataBlocks } = parsePkgTarZst(data);
|
|
75
|
-
if (!info.name) throw new Error('invalid .pkg.tar.zst: missing pkgname');
|
|
76
|
-
|
|
77
|
-
const db = loadDatabase();
|
|
78
|
-
const existing = getPackage(db, info.name);
|
|
79
|
-
if (opts.needed && existing && existing.version === info.version) return false;
|
|
80
|
-
|
|
81
|
-
if (!opts.noscriptlet && install?.pre_install) {
|
|
82
|
-
const script = `pre_install() {\n${install.pre_install}\n}\npost_install() { ${install.post_install || ''} }\npre_remove() { ${install.pre_remove || ''} }\npost_remove() { ${install.post_remove || ''} }\n`;
|
|
83
|
-
saveScript(info.name, '.INSTALL', script);
|
|
84
|
-
const tmpScript = `/var/lib/pacman-debian/info/${info.name}/.INSTALL`;
|
|
85
|
-
try { execSync(`/bin/bash -c 'source "${tmpScript}" && pre_install'`, { stdio: 'inherit' }); } catch {}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const files: string[] = [];
|
|
89
|
-
for (const entry of dataBlocks) {
|
|
90
|
-
const targetPath = path.resolve('/', entry.name);
|
|
91
|
-
if (!targetPath.startsWith('/')) continue;
|
|
92
|
-
files.push('/' + entry.name);
|
|
93
|
-
if (entry.data) {
|
|
94
|
-
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
95
|
-
fs.writeFileSync(targetPath, entry.data, { mode: 0o755 });
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (!opts.noscriptlet && install?.post_install) {
|
|
100
|
-
const tmpScript = `/var/lib/pacman-debian/info/${info.name}/.INSTALL`;
|
|
101
|
-
try { execSync(`/bin/bash -c 'source "${tmpScript}" && post_install'`, { stdio: 'inherit' }); } catch {}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const ip: InstalledPackage = {
|
|
105
|
-
name: info.name, version: info.version,
|
|
106
|
-
architecture: info.arch || 'any',
|
|
107
|
-
description: info.description || '',
|
|
108
|
-
depends: (info.depends || []).join(', '),
|
|
109
|
-
conflicts: (info.conflicts || []).join(', '),
|
|
110
|
-
provides: (info.provides || []).join(', '),
|
|
111
|
-
homepage: info.url,
|
|
112
|
-
controlSection: 'unknown', controlPriority: 'optional',
|
|
113
|
-
installedSize: info.installedSize,
|
|
114
|
-
installTime: Date.now(), reason, files, repoType: 'arch',
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
addPackage(db, ip);
|
|
118
|
-
try { writeDpkgEntry(ip); } catch (e) { console.error(' WARNING: failed to write dpkg status:', e); }
|
|
119
|
-
saveDatabase(db);
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export async function installPkgFile(filePath: string, reason: 'explicit' | 'dependency', opts: InstallOptions = {}): Promise<boolean> {
|
|
124
|
-
if (opts.print) { console.log(` would install: ${path.basename(filePath)}`); return true; }
|
|
125
|
-
if (filePath.endsWith('.pkg.tar.zst') || filePath.endsWith('.pkg.tar.xz') || filePath.endsWith('.pkg.tar.gz')) {
|
|
126
|
-
return installArch(filePath, reason, opts);
|
|
127
|
-
}
|
|
128
|
-
return installDeb(filePath, reason, opts);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export async function installPkg(target: string, opts: InstallOptions = {}): Promise<boolean> {
|
|
132
|
-
if (fs.existsSync(target) && ['.deb', '.pkg.tar.zst', '.pkg.tar.xz', '.pkg.tar.gz'].some(e => target.endsWith(e))) {
|
|
133
|
-
const cols = process.stdout.columns || 80;
|
|
134
|
-
const barLen = Math.max(Math.floor((cols - 30) * 0.35), 8);
|
|
135
|
-
const barDone = '#'.repeat(barLen);
|
|
136
|
-
const fname = path.basename(target).replace(/\.(pkg\.tar\.(zst|xz|gz)|deb)$/, '');
|
|
137
|
-
|
|
138
|
-
console.log(`Packages (1): ${fname}\n`);
|
|
139
|
-
if (!await confirm(':: Proceed with installation?')) return false;
|
|
140
|
-
if (opts.print) { console.log(` would install: ${path.basename(target)}`); return true; }
|
|
141
|
-
process.stdout.write(`(1/1) loading package data... ${barDone} 100%\n`);
|
|
142
|
-
process.stdout.write(`(1/1) installing ${fname.padEnd(25)}${barDone} 100%\n`);
|
|
143
|
-
return await installPkgFile(path.resolve(target), 'explicit', opts);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return (await installPackages([target], opts)) > 0;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export async function installPackages(targets: string[], opts: InstallOptions = {}): Promise<number> {
|
|
150
|
-
initDb();
|
|
151
|
-
|
|
152
|
-
// Validate targets exist
|
|
153
|
-
const targetPkgs: RepoPkg[] = [];
|
|
154
|
-
for (const t of targets) {
|
|
155
|
-
const rp = findInRepo(t);
|
|
156
|
-
if (!rp) {
|
|
157
|
-
const cacheDir = '/var/cache/pacman-debian/packages';
|
|
158
|
-
if (!fs.existsSync(cacheDir) || fs.readdirSync(cacheDir).length === 0) {
|
|
159
|
-
console.error('error: database not synced (run pacman -Sy)');
|
|
160
|
-
return 0;
|
|
161
|
-
}
|
|
162
|
-
console.error(`error: '${t}' not found`);
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (opts.needed && dpkgHasPackage(t)) {
|
|
166
|
-
console.log(` ${t} is up to date`);
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
targetPkgs.push(rp);
|
|
170
|
-
}
|
|
171
|
-
if (targetPkgs.length === 0) return 0;
|
|
172
|
-
|
|
173
|
-
// Resolve dependencies
|
|
174
|
-
console.log('resolving dependencies...');
|
|
175
|
-
const { install: depResults, errors: depErrors } = resolveDeps(targets);
|
|
176
|
-
for (const err of depErrors) console.error(` warning: ${err}`);
|
|
177
|
-
if (depErrors.length > 0 && depResults.length === 0) return 0;
|
|
178
|
-
|
|
179
|
-
// Dedupe: targets first, then deps
|
|
180
|
-
const allPkgs: RepoPkg[] = [];
|
|
181
|
-
const seen = new Set<string>();
|
|
182
|
-
for (const rp of targetPkgs) {
|
|
183
|
-
if (seen.has(rp.package)) continue;
|
|
184
|
-
seen.add(rp.package);
|
|
185
|
-
allPkgs.push(rp);
|
|
186
|
-
}
|
|
187
|
-
for (const dr of depResults) {
|
|
188
|
-
if (seen.has(dr.pkg.package)) continue;
|
|
189
|
-
seen.add(dr.pkg.package);
|
|
190
|
-
allPkgs.push(dr.pkg);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Conflict detection
|
|
194
|
-
console.log('looking for conflicting packages...\n');
|
|
195
|
-
const conflicts = detectConflicts(allPkgs);
|
|
196
|
-
for (const c of conflicts) {
|
|
197
|
-
console.error(` ${c.reason}`);
|
|
198
|
-
}
|
|
199
|
-
if (conflicts.length > 0) {
|
|
200
|
-
console.error('');
|
|
201
|
-
console.error('error: unresolvable package conflicts detected');
|
|
202
|
-
return 0;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const totalSize = allPkgs.reduce((s, p) => s + (p.size || 0), 0);
|
|
206
|
-
const totalInst = allPkgs.reduce((s, p) => s + ((p.installedSize || 0) * 1024), 0);
|
|
207
|
-
|
|
208
|
-
console.log(`Packages (${allPkgs.length}): ${allPkgs.map(p => p.package).join(' ')}\n`);
|
|
209
|
-
console.log(`Total Download Size: ${formatBytes(totalSize).padStart(9)}`);
|
|
210
|
-
console.log(`Total Installed Size: ${formatBytes(totalInst).padStart(9)}`);
|
|
211
|
-
console.log('');
|
|
212
|
-
|
|
213
|
-
if (!await confirm(':: Proceed with installation?')) return 0;
|
|
214
|
-
|
|
215
|
-
if (opts.print) {
|
|
216
|
-
for (const p of allPkgs) console.log(` would install: ${p.package}-${p.version}`);
|
|
217
|
-
return allPkgs.length;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const cols = process.stdout.columns || 80;
|
|
221
|
-
|
|
222
|
-
for (let i = 0; i < allPkgs.length; i++) {
|
|
223
|
-
const p = allPkgs[i];
|
|
224
|
-
const isExplicit = targetPkgs.some(r => r.package === p.package);
|
|
225
|
-
const nameMax = Math.max(20, cols - 60);
|
|
226
|
-
|
|
227
|
-
let prevTime = Date.now(), prevBytes = 0, smoothRate = 0;
|
|
228
|
-
const pname = p.package.length > nameMax ? p.package.slice(0, nameMax - 3) + '...' : p.package;
|
|
229
|
-
process.stdout.write(`${formatPfx(i + 1, allPkgs.length)}downloading ${pname}`);
|
|
230
|
-
|
|
231
|
-
const localPath = await downloadPkg(p, undefined, (rec, tot) => {
|
|
232
|
-
const now = Date.now();
|
|
233
|
-
const chunkSec = Math.max((now - prevTime) / 1000, 0.001);
|
|
234
|
-
const instant = (rec - prevBytes) / chunkSec;
|
|
235
|
-
smoothRate = smoothRate > 0 ? (instant + 2 * smoothRate) / 3 : instant;
|
|
236
|
-
prevTime = now; prevBytes = rec;
|
|
237
|
-
|
|
238
|
-
const dl = humanSize(rec, 1);
|
|
239
|
-
const rateS = formatRate(smoothRate);
|
|
240
|
-
const eta = smoothRate > 0 && tot > 0 ? (tot - rec) / smoothRate : 0;
|
|
241
|
-
const etaS = formatETA(eta);
|
|
242
|
-
const pct = tot > 0 ? Math.round(rec / tot * 100) : 0;
|
|
243
|
-
const bar = drawProgressBar(pct, cols);
|
|
244
|
-
process.stdout.write(`\r${formatPfx(i + 1, allPkgs.length)}${pname.padEnd(nameMax)}${dl.val.padStart(6)} ${dl.unit} ${rateS} ${etaS} [${bar}] ${String(pct).padStart(3)}%`);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
const barDone = drawProgressBar(100, cols);
|
|
248
|
-
process.stdout.write(`\r${formatPfx(i + 1, allPkgs.length)}checking package integrity ${barDone} 100%\n`);
|
|
249
|
-
process.stdout.write(`${formatPfx(i + 1, allPkgs.length)}loading package files ${barDone} 100%\n`);
|
|
250
|
-
process.stdout.write(`${formatPfx(i + 1, allPkgs.length)}installing ${pname.padEnd(nameMax)}${barDone} 100%\n`);
|
|
251
|
-
await installPkgFile(localPath, isExplicit ? (opts.asdeps ? 'dependency' : 'explicit') : 'dependency', opts);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return allPkgs.length;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function formatPfx(i: number, n: number): string {
|
|
258
|
-
return `(${i}/${n}) `;
|
|
259
|
-
}
|
|
260
|
-
|
package/src/ops/query.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import { execSync } from 'node:child_process';
|
|
3
|
-
import * as localdb from '../db/localdb';
|
|
4
|
-
import { loadDatabase } from '../db/database';
|
|
5
|
-
import { readDpkgStatus } from '../db/dpkg-compat';
|
|
6
|
-
import { searchRepo } from '../repo/repository';
|
|
7
|
-
import { t } from '../i18n';
|
|
8
|
-
|
|
9
|
-
export function listInstalled(filter?: string): void {
|
|
10
|
-
const dpkg = readDpkgStatus();
|
|
11
|
-
let pkgs = [...dpkg.values()];
|
|
12
|
-
if (filter) {
|
|
13
|
-
const lq = filter.toLowerCase();
|
|
14
|
-
pkgs = pkgs.filter(p => p.package.toLowerCase().includes(lq) || (p.description && p.description.toLowerCase().includes(lq)));
|
|
15
|
-
}
|
|
16
|
-
if (pkgs.length === 0) { console.log(t('no_pkgs_installed')); return; }
|
|
17
|
-
pkgs.sort((a, b) => a.package.localeCompare(b.package));
|
|
18
|
-
for (const p of pkgs) console.log(`${p.package} ${p.version}`);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function listExplicit(): void {
|
|
22
|
-
for (const p of localdb.getAllPackages()) {
|
|
23
|
-
if (p.reason === 'explicit') console.log(`${p.name} ${p.version}`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function listDeps(): void {
|
|
28
|
-
for (const p of localdb.getAllPackages()) {
|
|
29
|
-
if (p.reason === 'dependency') console.log(`${p.name} ${p.version}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function listOrphans(): void {
|
|
34
|
-
const needed = new Set<string>();
|
|
35
|
-
for (const p of localdb.getAllPackages()) {
|
|
36
|
-
const deps = (p.depends || '').split(',').map(s => s.trim().split(/\s/)[0]).filter(Boolean);
|
|
37
|
-
for (const d of deps) needed.add(d);
|
|
38
|
-
}
|
|
39
|
-
for (const p of localdb.getAllPackages()) {
|
|
40
|
-
if (p.reason === 'dependency' && !needed.has(p.name)) console.log(`${p.name} ${p.version}`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function checkIntegrity(name?: string): void {
|
|
45
|
-
if (name) {
|
|
46
|
-
const p = localdb.getPackage(name);
|
|
47
|
-
if (!p) { console.error(t('error_not_installed', name)); return; }
|
|
48
|
-
let missing = 0;
|
|
49
|
-
for (const f of p.files) { if (!fs.existsSync(f)) missing++; }
|
|
50
|
-
console.log(missing === 0 ? t('integrity_ok', name, String(p.files.length)) : t('integrity_warning', name, String(missing)));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
for (const p of localdb.getAllPackages()) {
|
|
54
|
-
let missing = 0;
|
|
55
|
-
for (const f of p.files) { if (!fs.existsSync(f)) missing++; }
|
|
56
|
-
if (missing > 0) console.log(t('integrity_warning_global', p.name, String(missing)));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function showInfo(name: string, fromRepo: boolean): void {
|
|
61
|
-
if (fromRepo) {
|
|
62
|
-
const r = searchRepo(name);
|
|
63
|
-
const p = r.find(x => x.package === name);
|
|
64
|
-
if (!p) { console.error(t('error_not_found', name)); return; }
|
|
65
|
-
console.log(t('info_repo', p.repo));
|
|
66
|
-
console.log(t('info_name', p.package));
|
|
67
|
-
console.log(t('info_version', p.version));
|
|
68
|
-
console.log(t('info_description', p.description || ''));
|
|
69
|
-
if (p.depends) console.log(t('info_depends', p.depends));
|
|
70
|
-
if (p.size) console.log(t('info_download_size', (p.size / 1024).toFixed(2) + ' KiB'));
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const dpkg = readDpkgStatus();
|
|
75
|
-
const p = dpkg.get(name);
|
|
76
|
-
if (!p) { console.error(t('error_was_not_found', name)); return; }
|
|
77
|
-
|
|
78
|
-
const our = localdb.getPackage(name);
|
|
79
|
-
const m = !!our;
|
|
80
|
-
|
|
81
|
-
console.log(t('info_name', p.package));
|
|
82
|
-
console.log(t('info_version', p.version));
|
|
83
|
-
console.log(t('info_description', p.description || ''));
|
|
84
|
-
console.log(t('info_architecture', p.architecture));
|
|
85
|
-
console.log(t('info_url', p.homepage || ''));
|
|
86
|
-
if (m && our) console.log(t(our.reason === 'explicit' ? 'info_install_reason_explicit' : 'info_install_reason_dep'));
|
|
87
|
-
if (!m) console.log(t('info_install_reason_dpkg'));
|
|
88
|
-
if (p.depends) console.log(t('info_depends', p.depends));
|
|
89
|
-
if (p.installedSize) console.log(t('info_installed_size', (p.installedSize / 1024).toFixed(2) + ' KiB'));
|
|
90
|
-
if (p.maintainer) console.log(t('info_packager', p.maintainer));
|
|
91
|
-
if (our) {
|
|
92
|
-
console.log(t('info_files', String(our.files.length)));
|
|
93
|
-
console.log(t('info_install_date', new Date(our.installTime).toISOString().slice(0, 10)));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function queryFile(fp: string): void {
|
|
98
|
-
const owner = localdb.getFileOwner(fp);
|
|
99
|
-
if (owner) { console.log(t('file_owned_by', fp, owner)); return; }
|
|
100
|
-
try {
|
|
101
|
-
const out = execSync(`dpkg -S ${fp} 2>/dev/null`, { encoding: 'utf8' });
|
|
102
|
-
console.log(out.trim());
|
|
103
|
-
} catch {
|
|
104
|
-
console.error(t('error_no_pkg_owns_file', fp));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function listFiles(name: string): void {
|
|
109
|
-
const p = localdb.getPackage(name);
|
|
110
|
-
if (p) { for (const f of p.files) console.log(`${name} ${f}`); return; }
|
|
111
|
-
const lp = `/var/lib/dpkg/info/${name}.list`;
|
|
112
|
-
if (fs.existsSync(lp)) {
|
|
113
|
-
for (const f of fs.readFileSync(lp, 'utf8').split('\n').filter(Boolean)) console.log(`${name} ${f}`);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
console.error(t('error_was_not_found', name));
|
|
117
|
-
}
|
package/src/ops/remove.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import { initDb, loadDatabase, saveDatabase, removePkg, getPackage, runScript } from '../db/database';
|
|
3
|
-
import { removeDpkgEntry } from '../db/dpkg-compat';
|
|
4
|
-
import { confirm } from '../ui/prompt';
|
|
5
|
-
import type { RemoveOptions } from '../core/options';
|
|
6
|
-
import { t } from '../i18n';
|
|
7
|
-
|
|
8
|
-
function removeSingle(name: string, opts: RemoveOptions = {}): boolean {
|
|
9
|
-
initDb();
|
|
10
|
-
const db = loadDatabase();
|
|
11
|
-
const pkg = getPackage(db, name);
|
|
12
|
-
if (!pkg) { console.error(t('error_not_installed', name)); return false; }
|
|
13
|
-
|
|
14
|
-
if (opts.recursive && !opts.nodeps) {
|
|
15
|
-
const deps = (pkg.depends || '').split(',').map(s => s.trim().split(/\s/)[0]).filter(Boolean);
|
|
16
|
-
for (const d of deps) {
|
|
17
|
-
const dp = getPackage(db, d);
|
|
18
|
-
if (dp && (dp.reason === 'dependency' || opts.cascade)) {
|
|
19
|
-
removeSingle(d, { ...opts, recursive: false });
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (opts.cascade) {
|
|
25
|
-
const db = loadDatabase();
|
|
26
|
-
for (const [pname, ppkg] of db.packages) {
|
|
27
|
-
if (ppkg.depends && ppkg.depends.includes(name)) {
|
|
28
|
-
removeSingle(pname, { ...opts, recursive: false, cascade: true });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (!opts.noscriptlet) {
|
|
34
|
-
runScript(name, 'prerm', ['remove']);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
for (const f of pkg.files) {
|
|
38
|
-
try {
|
|
39
|
-
if (fs.existsSync(f)) {
|
|
40
|
-
const s = fs.lstatSync(f);
|
|
41
|
-
if (s.isDirectory()) { try { fs.rmdirSync(f); } catch {} }
|
|
42
|
-
else fs.unlinkSync(f);
|
|
43
|
-
}
|
|
44
|
-
} catch {}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!opts.noscriptlet) {
|
|
48
|
-
runScript(name, 'postrm', ['remove']);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
try { removeDpkgEntry(name); } catch {}
|
|
52
|
-
removePkg(db, name);
|
|
53
|
-
saveDatabase(db);
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export async function removeByName(name: string, opts: RemoveOptions = {}): Promise<boolean> {
|
|
58
|
-
initDb();
|
|
59
|
-
const db = loadDatabase();
|
|
60
|
-
const pkg = getPackage(db, name);
|
|
61
|
-
if (!pkg) { console.error(t('error_not_installed', name)); return false; }
|
|
62
|
-
|
|
63
|
-
if (opts.print) { console.log(t('would_remove', name)); return true; }
|
|
64
|
-
|
|
65
|
-
console.log(t('checking_deps_remove') + '\n');
|
|
66
|
-
console.log(t('packages_single', `${name}-${pkg.version}`));
|
|
67
|
-
console.log('');
|
|
68
|
-
|
|
69
|
-
if (!await confirm(':: Proceed with removal?', false)) return false;
|
|
70
|
-
|
|
71
|
-
const cols = process.stdout.columns || 80;
|
|
72
|
-
const bar = '#'.repeat(Math.max(Math.floor((cols - 45) * 0.35), 8));
|
|
73
|
-
removeSingle(name, opts);
|
|
74
|
-
process.stdout.write(t('progress_removing', '1', '1', name, bar) + '\n');
|
|
75
|
-
console.log(t('pkg_removed', name));
|
|
76
|
-
return true;
|
|
77
|
-
}
|
package/src/ops/upgrade.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { readDpkgStatus } from '../db/dpkg-compat';
|
|
4
|
-
import { syncRepos, findInRepo, downloadPkg } from '../repo/repository';
|
|
5
|
-
import { installPkgFile, installPackages } from './install';
|
|
6
|
-
import type { RepoPkg } from '../core/types';
|
|
7
|
-
import type { InstallOptions } from '../core/options';
|
|
8
|
-
import { confirm } from '../ui/prompt';
|
|
9
|
-
import { formatBytes } from '../ui/format';
|
|
10
|
-
import { t } from '../i18n';
|
|
11
|
-
|
|
12
|
-
const LOCAL_DIR = '/var/lib/pacman-debian/local';
|
|
13
|
-
|
|
14
|
-
interface UpgradeTarget {
|
|
15
|
-
name: string;
|
|
16
|
-
oldVer: string;
|
|
17
|
-
newVer: string;
|
|
18
|
-
pkg: RepoPkg;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function listInstalledFromLocal(): Map<string, string> {
|
|
22
|
-
const result = new Map<string, string>();
|
|
23
|
-
if (!fs.existsSync(LOCAL_DIR)) return result;
|
|
24
|
-
for (const entry of fs.readdirSync(LOCAL_DIR)) {
|
|
25
|
-
if (entry === 'by-name') continue;
|
|
26
|
-
const descPath = path.join(LOCAL_DIR, entry, 'desc');
|
|
27
|
-
if (!fs.existsSync(descPath)) continue;
|
|
28
|
-
try {
|
|
29
|
-
const data = JSON.parse(fs.readFileSync(descPath, 'utf8'));
|
|
30
|
-
if (data.name && data.version) result.set(data.name, data.version);
|
|
31
|
-
else if (data.package && data.version) result.set(data.package, data.version);
|
|
32
|
-
} catch {}
|
|
33
|
-
}
|
|
34
|
-
return result;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function collectUpgradeCandidates(): Promise<UpgradeTarget[]> {
|
|
38
|
-
const installed = listInstalledFromLocal();
|
|
39
|
-
const targets: UpgradeTarget[] = [];
|
|
40
|
-
|
|
41
|
-
for (const [name, oldVer] of installed) {
|
|
42
|
-
const pkg = findInRepo(name);
|
|
43
|
-
if (!pkg) continue;
|
|
44
|
-
const newVer = pkg.version;
|
|
45
|
-
if (newVer && newVer !== oldVer) {
|
|
46
|
-
targets.push({ name, oldVer, newVer, pkg });
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return targets;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export async function syncAndUpgrade(opts: InstallOptions = {}, force = false): Promise<void> {
|
|
53
|
-
process.stdout.write(t('syncing_databases') + '\n');
|
|
54
|
-
await syncRepos(force);
|
|
55
|
-
await doUpgrade(opts);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function doUpgrade(opts: InstallOptions = {}): Promise<void> {
|
|
59
|
-
console.log(t('starting_upgrade'));
|
|
60
|
-
const targets = await collectUpgradeCandidates();
|
|
61
|
-
if (targets.length === 0) { console.log(t('nothing_to_do')); return; }
|
|
62
|
-
console.log(`\nPackages (${targets.length}):`);
|
|
63
|
-
for (const t_ of targets) console.log(` ${t_.name} ${t_.oldVer} -> ${t_.newVer}`);
|
|
64
|
-
console.log('');
|
|
65
|
-
if (!await confirm(':: Proceed with upgrade?')) { return; }
|
|
66
|
-
|
|
67
|
-
if (opts.print) {
|
|
68
|
-
for (const t_ of targets) console.log(` would upgrade: ${t_.name} ${t_.oldVer} -> ${t_.newVer}`);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
for (let i = 0; i < targets.length; i++) {
|
|
73
|
-
const t_ = targets[i];
|
|
74
|
-
console.log(`(${i + 1}/${targets.length}) downloading ${t_.name}...`);
|
|
75
|
-
const rp = findInRepo(t_.name);
|
|
76
|
-
if (!rp) { console.error(` WARNING: ${t_.name} not found in repo`); continue; }
|
|
77
|
-
const localPath = await downloadPkg(rp);
|
|
78
|
-
console.log(`(${i + 1}/${targets.length}) checking package integrity...`);
|
|
79
|
-
console.log(`(${i + 1}/${targets.length}) loading package files...`);
|
|
80
|
-
console.log(`(${i + 1}/${targets.length}) upgrading ${t_.name}...`);
|
|
81
|
-
await installPkgFile(localPath, 'explicit', opts);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export async function upgradeOnly(opts: InstallOptions = {}): Promise<void> {
|
|
86
|
-
await doUpgrade(opts);
|
|
87
|
-
}
|
package/src/repo/config.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import type { Config, RepoConfig } from '../core/types';
|
|
4
|
-
|
|
5
|
-
const CONFIG_PATHS = ['/etc/pacman-debian/pacman.conf', '/etc/pacman/pacman.conf', '/etc/pacman.conf'];
|
|
6
|
-
const INCLUDE_DIR = '/etc/pacman-debian';
|
|
7
|
-
|
|
8
|
-
function findConfig(): string {
|
|
9
|
-
for (const p of CONFIG_PATHS) {
|
|
10
|
-
if (fs.existsSync(p)) return p;
|
|
11
|
-
}
|
|
12
|
-
return CONFIG_PATHS[0]; // fallback to default
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function parseKeyValue(line: string): [string, string] | null {
|
|
16
|
-
const eq = line.indexOf('=');
|
|
17
|
-
if (eq === -1) return null;
|
|
18
|
-
return [line.slice(0, eq).trim().toLowerCase(), line.slice(eq + 1).trim()];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function applyKeyValue(cur: RepoConfig, key: string, value: string, isOverride: boolean): void {
|
|
22
|
-
if (key === 'server' && (isOverride || !cur.server)) cur.server = value;
|
|
23
|
-
else if (key === 'type' && (isOverride || !cur.type)) cur.type = value === 'arch' ? 'arch' : 'debian';
|
|
24
|
-
else if (key === 'dist' && (isOverride || !cur.dist)) cur.dist = value;
|
|
25
|
-
else if (key === 'components' && (isOverride || !cur.components?.length)) cur.components = value.split(/\s+/).filter(Boolean);
|
|
26
|
-
else if (key === 'dbfile' && (isOverride || !cur.dbFile)) cur.dbFile = value;
|
|
27
|
-
else if (key === 'architecture' && (isOverride || !cur.architecture)) cur.architecture = value;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function loadRepoFile(filePath: string): Record<string, string> {
|
|
31
|
-
const result: Record<string, string> = {};
|
|
32
|
-
if (!fs.existsSync(filePath)) return result;
|
|
33
|
-
for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) {
|
|
34
|
-
const t = line.trim();
|
|
35
|
-
if (!t || t.startsWith('#')) continue;
|
|
36
|
-
const kv = parseKeyValue(t);
|
|
37
|
-
if (kv) result[kv[0]] = kv[1];
|
|
38
|
-
}
|
|
39
|
-
return result;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function loadConfig(): Config {
|
|
43
|
-
const nativeArch = process.arch === 'arm64' ? 'aarch64' : process.arch;
|
|
44
|
-
const cfg: Config = { architecture: nativeArch, color: false, repos: [] };
|
|
45
|
-
const configPath = findConfig();
|
|
46
|
-
if (!fs.existsSync(configPath)) {
|
|
47
|
-
cfg.repos.push({ name: 'ubuntu', type: 'debian', server: 'http://ports.ubuntu.com/ubuntu-ports', dist: 'noble', components: ['main', 'universe'] });
|
|
48
|
-
return cfg;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const content = fs.readFileSync(configPath, 'utf8');
|
|
52
|
-
let cur: RepoConfig | null = null;
|
|
53
|
-
let inOptions = false;
|
|
54
|
-
|
|
55
|
-
for (const line of content.split('\n')) {
|
|
56
|
-
const t = line.trim();
|
|
57
|
-
if (!t || t.startsWith('#')) continue;
|
|
58
|
-
const sm = t.match(/^\[(.+)\]$/);
|
|
59
|
-
if (sm) {
|
|
60
|
-
if (cur) { if (!cur.type) cur.type = 'debian'; cfg.repos.push(cur); }
|
|
61
|
-
const n = sm[1];
|
|
62
|
-
if (n === 'options') { cur = null; inOptions = true; continue; }
|
|
63
|
-
cur = { name: n, server: '', dist: '', components: [] };
|
|
64
|
-
inOptions = false;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
const kv = parseKeyValue(t);
|
|
68
|
-
if (!kv) {
|
|
69
|
-
// Bare boolean flag (no = sign), e.g. "Color"
|
|
70
|
-
const flag = t.trim().toLowerCase();
|
|
71
|
-
if (flag === 'color' && (inOptions || !cur)) cfg.color = true;
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
const [k, v] = kv;
|
|
75
|
-
|
|
76
|
-
if (inOptions || !cur) {
|
|
77
|
-
if (k === 'architecture') cfg.architecture = v;
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (k === 'include') {
|
|
82
|
-
// Resolve include path (relative to config dir)
|
|
83
|
-
const incPath = v.startsWith('/') ? v : path.join(INCLUDE_DIR, v);
|
|
84
|
-
const included = loadRepoFile(incPath);
|
|
85
|
-
for (const [ik, iv] of Object.entries(included)) {
|
|
86
|
-
applyKeyValue(cur, ik, iv, false);
|
|
87
|
-
}
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
applyKeyValue(cur, k, v, true);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (cur) { if (!cur.type) cur.type = 'debian'; cfg.repos.push(cur); }
|
|
95
|
-
return cfg;
|
|
96
|
-
}
|