Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
mv pkgm.ts pkgm
tar czf pkgm-$V.tgz pkgm

- uses: pkgxdev/setup@v3
- uses: pkgxdev/setup@v4

- name: verify `pkgm --version`
run: test "$(./pkgm --version)" = "pkgm $V"
Expand Down
16 changes: 15 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
#TODO test on linux! we are currently broken due to rpath issues
# https://github.com/pkgxdev/pkgm/pull/30#issuecomment-2678957666
test:
continue-on-error: true
strategy:
matrix:
os:
Expand All @@ -33,7 +34,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: pkgxdev/setup@v3
- uses: pkgxdev/setup@v4

- run: ./pkgm.ts i git
- run: ~/.local/bin/git --version
Expand Down Expand Up @@ -90,6 +91,19 @@ jobs:
env:
XDG_DATA_HOME: /tmp/foo

- run: |
set -x
sudo ./pkgm.ts i node@22 dev
[[ $(node --version) = v22* ]] || exit 2
mkdir foo
cd foo
echo "dependencies: node@20" > pkgx.yaml
mkdir -p /tmp/bar/pkgx/dev$PWD/
touch /tmp/bar/pkgx/dev$PWD/dev.pkgx.activated # `dev .` doesn’t work in CI (fix in dev^2)
[[ $(node --version) = v20* ]] || exit 3
env:
XDG_DATA_HOME: /tmp/bar

# https://github.com/pkgxdev/pkgm/issues/62
- run: |
./pkgm.ts i spotify_player
Expand Down
55 changes: 33 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,26 @@ Install `pkgx` packages to `/usr/local`.
> `pkgm` is new software. Please report any issues you encounter and try it out
> in parallel with your current package manager.

> [!WARNING]
>
> `pkgm` is new software. Some features listed here are not yet implemented. You
> can help! Or we’ll get to it soon.

## Usage

```sh
$ pkgm install git
# ^^ installs latest git to ~/.local. ie. you get ~/.local/bin/git
$ pkgm install node
# ^^ installs latest node to ~/.local. ie. you get ~/.local/bin/node

$ pkgm install git@2.41
# ^^ installs git^2.41 or switches out the installed git to 2.41
$ pkgm install node@20.1
# ^^ installs node^20.1 or switches out the installed node to 20.1

$ pkgm uninstall git
$ pkgm uninstall node

$ sudo pkgm install git
# ^^ installs git to /usr/local. ie. you get /usr/local/bin/git
$ sudo pkgm install node
# ^^ installs node to /usr/local. ie. you get /usr/local/bin/node

$ pkgm shim git
# ^^ creates a shim for git in ~/.local/bin
# these shims mimic the pkgx v1 lazy-loading shims, and are desirable for
# certain types of self-healing and dev-setup containers, among other things
# requires pkgx^2.4.0 for --shebang option
$ pkgm shim node
# ^^ creates a shim for node at ~/.local/bin/node
# see the docs below for details about shims

$ pkgm list
# ^^ lists what is installed
# ^^ lists what’s installed

$ pkgm outdated
# ^^ lists outdated installations
Expand All @@ -43,9 +36,6 @@ $ pkgm update

$ sudo pkgm update
# ^^ updates /usr/local packages to latest versions

$ pkgm pin git
# ^^ prevents the installed git from being updated
```

> [!TIP]
Expand All @@ -55,6 +45,25 @@ $ pkgm pin git
> - `pkgm ls` is an alias for `pkgm list`
> - `pkgm up` is an alias for `pkgm update`

> [!WARNING]
>
> You should probably `sudo pkgm install` rather than install to `~/.local`.
> This is because many other tools will not look in `~/.local` for packages
> _even_ if it’s in `PATH`. Having said this—by all means—see how it goes!

> ### Shims
>
> Shims are files with a single line, eg `#!/usr/bin/env -S pkgx -q! node@22`.
>
> Thus using the shell to reinvoke the file via `pkgx`. You get all the benefits
> of an installed package without actually it actually being installed until it
> is needed. Traits desirable for certain types of self-healing, devops
> containers and plenty more one-off or ephemeral tasks.
>
> Shims are pretty great—but have caveats. Some software might be surprised that
> a package is not fully “installed” and lead to errors. In practice we have
> seen issues only rarely and for more complex package combinations.

## Installation

```sh
Expand All @@ -78,4 +87,6 @@ brew rm pkgm || sudo rm /usr/local/bin/pkgm
- Blazingly fast
- Install specific versions of any pkg
- You install by executable name—thus you _don’t have to do a search first_
- Installed packages are installed as `root`
- Installed packages can be installed as `root`
- `dev`-aware installations
- Self-healing shims
53 changes: 50 additions & 3 deletions pkgm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,14 @@ async function install(args: string[], basePath: string) {
`${x.pkg.project}/v${x.pkg.version}`
);

const pkgx_dir = Deno.env.get("PKGX_DIR") || `${Deno.env.get("HOME")}/.pkgx`;
// get the pkgx_dir this way as it is a) more reliable and b) the only way if
// we are running as sudo on linux since it doesn’t give us a good way to get
// the home directory of the pre-sudo user
const pkgx_dir = (() => {
const { path, pkg } = json.pkgs[0]!;
const remove = pkg.project + "/v" + pkg.version;
return path.string.slice(0, -remove.length - 1);
})();

const runtime_env = expand_runtime_env(json, basePath);

Expand Down Expand Up @@ -178,10 +185,13 @@ async function install(args: string[], basePath: string) {
for (const [key, value] of Object.entries(env)) {
sh += `export ${key}="${value}"\n`;
}
sh += `exec "${bin_prefix}/${entry.name}" "$@"\n`;

sh += "\n";
//TODO should be specific with the project
sh += dev_stub_text(to_stub, bin_prefix, entry.name);

await Deno.remove(to_stub); //FIXME inefficient to symlink for no reason
await Deno.writeTextFile(to_stub, sh);
await Deno.writeTextFile(to_stub, sh.trim() + "\n");
await Deno.chmod(to_stub, 0o755);

rv.push(to_stub);
Expand Down Expand Up @@ -282,6 +292,7 @@ async function query_pkgx(
set("PKGX_DIR");
set("PKGX_PANTRY_DIR");
set("PKGX_DIST_URL");
set("XDG_DATA_HOME");

const needs_sudo_backwards = install_prefix().string == "/usr/local";
let cmd = needs_sudo_backwards ? "/usr/bin/sudo" : pkgx;
Expand Down Expand Up @@ -316,6 +327,7 @@ async function query_pkgx(

const out = await proc.output();
const json = JSON.parse(new TextDecoder().decode(out.stdout));

const pkgs =
(json.pkgs as { path: string; project: string; version: string }[]).map(
(x) => {
Expand Down Expand Up @@ -726,3 +738,38 @@ function install_prefix() {
return Path.home().join(".local");
}
}

function dev_stub_text(selfpath: string, bin_prefix: string, name: string) {
if (selfpath.startsWith("/usr/local") && selfpath != "/usr/local/bin/dev") {
return `
dev_check() {
[ -x /usr/local/bin/dev ] || return 1
local d="$PWD"
until [ "$d" = / ]; do
if [ -f "${datadir()}/pkgx/dev/$d/dev.pkgx.activated" ]; then
echo $d
return 0
fi
d="$(dirname "$d")"
done
return 1
}

if d="$(dev_check)"; then
eval "$(/usr/local/bin/dev "$d" 2>/dev/null)"
[ "$(command -v ${name} 2>/dev/null)" != "${selfpath}" ] && exec ${name} "$@"
fi

exec ${bin_prefix}/${name} "$@"
`.trim();
} else {
return `exec ${bin_prefix}/${name} "$@"`;
}
}

function datadir() {
const default_data_home = Deno.build.os == "darwin"
? "/Library/Application Support"
: "/.local/share";
return `\${XDG_DATA_HOME:-$HOME${default_data_home}}`;
}
Loading