Skip to content

Commit b89485e

Browse files
authored
Merge pull request #38 from pkgxdev/pkgm-ls-rm
`pkgm ls` & `pkgm rm`
2 parents e32c599 + 202deb7 commit b89485e

2 files changed

Lines changed: 135 additions & 8 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: 132 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
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 { 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";
4-
import { ensureDir, existsSync } from "jsr:@std/fs@^1";
8+
import { ensureDir, existsSync, walk } from "jsr:@std/fs@^1";
59
import { parseArgs } from "jsr:@std/cli@^1";
610

711
function standardPath() {
@@ -58,24 +62,28 @@ if (parsedArgs.help) {
5862
case "li":
5963
await install(args, `${Deno.env.get("HOME")!}/.local`);
6064
break;
65+
case "stub":
6166
case "shim":
6267
// this uses the old behavior of pkgx v1, which is to install to ~/.local/bin
6368
// if we want to write to /usr/local, we need to use sudo
6469
await shim(args, `${Deno.env.get("HOME")!}/.local`);
65-
// await shim(args, "/usr/local");
6670
break;
67-
// case "local-shim":
68-
// await shim(args, `${Deno.env.get("HOME")!}/.local`);
69-
// break;
7071
case "uninstall":
7172
case "rm":
73+
for (const arg of args) {
74+
await uninstall(arg);
75+
}
76+
break;
7277
case "list":
7378
case "ls":
79+
for await (const path of ls()) {
80+
console.log(path);
81+
}
82+
break;
7483
case "up":
7584
case "update":
7685
case "pin":
7786
case "outdated":
78-
case "stub":
7987
console.error("%cunimplemented. soz. U EARLY.", "color: red");
8088
Deno.exit(1);
8189
break;
@@ -422,3 +430,119 @@ function get_pkgx() {
422430
}
423431
throw new Error("no `pkgx` found in `$PATH`");
424432
}
433+
434+
async function* ls() {
435+
for (
436+
const path of [new Path("/usr/local/pkgs"), Path.home().join(".local/pkgs")]
437+
) {
438+
if (!path.isDirectory()) continue;
439+
const dirs = [path];
440+
let dir: Path | undefined;
441+
while ((dir = dirs.pop()) != undefined) {
442+
for await (const [path, { name, isDirectory, isSymlink }] of dir.ls()) {
443+
if (!isDirectory || isSymlink) continue;
444+
if (/^v\d+\./.test(name)) {
445+
yield path;
446+
} else {
447+
dirs.push(path);
448+
}
449+
}
450+
}
451+
}
452+
}
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)