Skip to content

Commit 202deb7

Browse files
committed
pkgm uninstall
1 parent 7db4122 commit 202deb7

2 files changed

Lines changed: 114 additions & 4 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ jobs:
4242
4343
- run: ./pkgm.ts i git
4444
- run: /usr/local/bin/git --version
45+
- run: ./pkgm.ts ls | grep /usr/local/pkgs/git-scm.org
46+
- run: ./pkgm.ts rm git
47+
- run: test ! -f /usr/local/bin/git
4548

4649
- run: ./pkgm.ts i pkgx.sh/brewkit
4750
- run: /usr/local/bin/bk --help

pkgm.ts

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
#!/usr/bin/env -S pkgx --quiet deno^2.1 run --ext=ts --allow-sys=uid --allow-run --allow-env=PKGX_DIR,HOMEBREW_PREFIX,HOME,PATH --allow-read --allow-write
2-
import { Path, SemVer, semver } from "https://deno.land/x/libpkgx@v0.20.3/mod.ts";
1+
#!/usr/bin/env -S pkgx --quiet deno^2.1 run --ext=ts --allow-sys=uid --allow-run --allow-env --allow-read --allow-write --allow-ffi
2+
import {
3+
Path,
4+
SemVer,
5+
semver,
6+
} from "https://deno.land/x/libpkgx@v0.20.3/mod.ts";
37
import { dirname, fromFileUrl, join } from "jsr:@std/path@^1";
48
import { ensureDir, existsSync, walk } from "jsr:@std/fs@^1";
59
import { parseArgs } from "jsr:@std/cli@^1";
@@ -66,6 +70,10 @@ if (parsedArgs.help) {
6670
break;
6771
case "uninstall":
6872
case "rm":
73+
for (const arg of args) {
74+
await uninstall(arg);
75+
}
76+
break;
6977
case "list":
7078
case "ls":
7179
for await (const path of ls()) {
@@ -424,10 +432,13 @@ function get_pkgx() {
424432
}
425433

426434
async function* ls() {
427-
for (const path of [Path.root.join("/usr/local/pkgs"), Path.home().join(".local/pkgs")]) {
435+
for (
436+
const path of [new Path("/usr/local/pkgs"), Path.home().join(".local/pkgs")]
437+
) {
438+
if (!path.isDirectory()) continue;
428439
const dirs = [path];
429440
let dir: Path | undefined;
430-
while (dir = dirs.pop()) {
441+
while ((dir = dirs.pop()) != undefined) {
431442
for await (const [path, { name, isDirectory, isSymlink }] of dir.ls()) {
432443
if (!isDirectory || isSymlink) continue;
433444
if (/^v\d+\./.test(name)) {
@@ -439,3 +450,99 @@ async function* ls() {
439450
}
440451
}
441452
}
453+
454+
import { hooks, plumbing } from "https://deno.land/x/libpkgx@v0.20.3/mod.ts";
455+
456+
async function uninstall(arg: string) {
457+
let found: { project: string } | undefined =
458+
(await hooks.usePantry().find(arg))?.[0];
459+
if (!found) {
460+
found = await plumbing.which(arg);
461+
}
462+
if (!found) throw new Error(`pkg not found: ${arg}`);
463+
464+
const set = new Set<string>();
465+
const files: Path[] = [];
466+
let dirs: Path[] = [];
467+
const pkg_dirs: Path[] = [];
468+
for (const root of [new Path("/usr/local"), Path.home().join(".local")]) {
469+
const dir = root.join("pkgs", found.project).isDirectory();
470+
if (!dir) continue;
471+
pkg_dirs.push(dir);
472+
for await (const [pkgdir, { isDirectory }] of dir.ls()) {
473+
if (!isDirectory) continue;
474+
for await (const { path, isDirectory } of walk(pkgdir.string)) {
475+
const leaf = new Path(path).relative({ to: pkgdir });
476+
const resolved_path = root.join(leaf);
477+
if (set.has(resolved_path.string)) continue;
478+
if (!resolved_path.exists()) continue;
479+
if (isDirectory) {
480+
dirs.push(resolved_path);
481+
} else {
482+
files.push(resolved_path);
483+
}
484+
}
485+
}
486+
}
487+
488+
// we need to delete this in a heirachical fashion or they don’t delete
489+
dirs = dirs.sort().reverse();
490+
491+
if (files.length == 0) {
492+
console.error("not installed");
493+
Deno.exit(1);
494+
}
495+
496+
const needs_sudo = files.some((p) => p.string.startsWith("/usr/local"));
497+
if (needs_sudo) {
498+
{
499+
const { success, code } = await new Deno.Command("/usr/bin/sudo", {
500+
args: ["rm", ...files.map((p) => p.string)],
501+
}).spawn().status;
502+
if (!success) Deno.exit(code);
503+
}
504+
{
505+
await new Deno.Command("/usr/bin/sudo", {
506+
args: ["rmdir", ...dirs.map((p) => p.string)],
507+
stderr: "null",
508+
}).spawn().status;
509+
}
510+
511+
const { success, code } = await new Deno.Command("/usr/bin/sudo", {
512+
args: [
513+
"rm",
514+
"-rf",
515+
...pkg_dirs.map((p) => p.string),
516+
...pkg_dirs.map((x) => x.parent().string),
517+
],
518+
}).spawn().status;
519+
if (!success) Deno.exit(code);
520+
521+
await new Deno.Command("/usr/bin/sudo", {
522+
args: [
523+
"rmdir",
524+
"/usr/local/pkgs",
525+
Path.home().join(".local/pkgs").string,
526+
],
527+
stderr: "null",
528+
}).spawn().status;
529+
} else {
530+
for (const path of files) {
531+
if (!path.isDirectory()) {
532+
Deno.removeSync(path.string);
533+
}
534+
}
535+
for (const path of dirs) {
536+
if (path.isDirectory()) {
537+
try {
538+
Deno.removeSync(path.string);
539+
} catch {
540+
// some dirs will not be removable
541+
}
542+
}
543+
}
544+
for (const path of pkg_dirs) {
545+
Deno.removeSync(path.string, { recursive: true });
546+
}
547+
}
548+
}

0 commit comments

Comments
 (0)