diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index db878a8..bf1b1e1 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -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" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 832b9bc..f009f64 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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: @@ -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 @@ -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 diff --git a/README.md b/README.md index fc5920b..74753e1 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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] @@ -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 @@ -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 diff --git a/pkgm.ts b/pkgm.ts index 73c9834..d4fc358 100755 --- a/pkgm.ts +++ b/pkgm.ts @@ -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); @@ -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); @@ -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; @@ -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) => { @@ -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}}`; +}