Skip to content

Commit 2b8a341

Browse files
committed
pkgm outdated
Really we need to record what constraint the user requested when installing as well as what packages were explicitly installed in order to do a better job here. Currently we assume any ^x that is newer counts as outdated provided it is not a package that is a dependency with a tighter constraint range. This also ties into how we handle multiple installs, which currently we do, eg. you can have openssl^1 and openssl^3 both installed though this works by luck rather than by design. If you have both then we need to state that both ^1 and ^3 are outdated, this naive implementation will actually error currently for this case.
1 parent b28a097 commit 2b8a341

1 file changed

Lines changed: 61 additions & 3 deletions

File tree

pkgm.ts

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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
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 --allow-net=dist.pkgx.dev
22
import {
33
hooks,
44
Installation,
@@ -10,6 +10,7 @@ import {
1010
import { dirname, fromFileUrl, join } from "jsr:@std/path@^1";
1111
import { ensureDir, existsSync, walk } from "jsr:@std/fs@^1";
1212
import { parseArgs } from "jsr:@std/cli@^1";
13+
import hydrate from "https://deno.land/x/libpkgx@v0.20.3/src/plumbing/hydrate.ts";
1314

1415
function standardPath() {
1516
let path = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
@@ -87,8 +88,7 @@ if (parsedArgs.help) {
8788
case "update":
8889
case "pin":
8990
case "outdated":
90-
console.error("%cunimplemented. soz. U EARLY.", "color: red");
91-
Deno.exit(1);
91+
await outdated();
9292
break;
9393
case "sudo-install": {
9494
const [pkgx_dir, runtime_env, basePath, ...paths] = args;
@@ -591,3 +591,61 @@ function writable(path: string) {
591591
return false;
592592
}
593593
}
594+
595+
async function outdated() {
596+
const pkgs: Installation[] = [];
597+
for await (const pkg of walk_pkgs()) {
598+
pkgs.push(pkg);
599+
}
600+
601+
const { pkgs: raw_graph } = await hydrate(
602+
pkgs.map((x) => ({
603+
project: x.pkg.project,
604+
constraint: new semver.Range(`^${x.pkg.version}`),
605+
})),
606+
);
607+
const graph: Record<string, semver.Range> = {};
608+
for (const { project, constraint } of raw_graph) {
609+
graph[project] = constraint;
610+
}
611+
612+
for (const { path, pkg } of pkgs) {
613+
const versions = await hooks.useInventory().get(pkg);
614+
// console.log(pkg, graph[pkg.project]);
615+
const constrained_versions = versions.filter((x) =>
616+
graph[pkg.project].satisfies(x) && x.gt(pkg.version)
617+
);
618+
if (constrained_versions.length) {
619+
console.log(
620+
pkg.project,
621+
"is outdated",
622+
pkg.version,
623+
"<",
624+
constrained_versions.slice(-1)[0],
625+
`\x1b[2m${path}\x1b[22m`,
626+
);
627+
}
628+
}
629+
}
630+
631+
async function* walk_pkgs() {
632+
for (
633+
const root of [new Path("/usr/local/pkgs"), Path.home().join(".local/bin")]
634+
) {
635+
const dirs = [root];
636+
let dir: Path | undefined;
637+
while ((dir = dirs.pop()) !== undefined) {
638+
if (!dir.isDirectory()) continue;
639+
for await (const [path, { name, isSymlink, isDirectory }] of dir.ls()) {
640+
if (isSymlink || !isDirectory) continue;
641+
if (semver.parse(name)) {
642+
const project = path.parent().relative({ to: root });
643+
const version = new SemVer(path.basename());
644+
yield { path, pkg: { project, version } };
645+
} else {
646+
dirs.push(path);
647+
}
648+
}
649+
}
650+
}
651+
}

0 commit comments

Comments
 (0)