Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(install): add support for --unsafe install flag #1008

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

rustdevbtw
Copy link
Contributor

@rustdevbtw rustdevbtw commented May 11, 2024

closes #997
closes #991

This PR adds support for the --unsafe install flag to PKGX, which, as proposed by @jhheider (thanks!), creates additional env stubs for a package on the first run, making subsequent runs make use of the already installed binaries, thus making it faster[1].

The stub

As for the stub, it makes it a bit different, for instance, here's a "safe" node wrapper (installed normally):

#!/bin/sh
if [ "$PKGX_UNINSTALL" != 1 ]; then
  exec pkgx +nodejs.org -- node "$@"
else
  cd "$(dirname "$0")"
  rm node && echo "uninstalled: nodejs.org" >&2
fi

And here's the "unsafe" (as in experimental) node wrapper:

#!/bin/sh
if [ "$PKGX_UNINSTALL" != 1 ]; then
-  exec pkgx +nodejs.org -- node "$@"
+  ARGS="$@"
+  pkgx_resolve() {
+    mkdir -p "${XDG_CACHE_DIR:-$HOME/.cache}/pkgx/envs"
+    pkgx +nodejs.org 1>"${XDG_CACHE_DIR:-$HOME/.cache}/pkgx/envs/nodejs.org.env"
+    run
+  }
+  run() {
+    if [[ -e "${XDG_CACHE_DIR:-$HOME/.cache}/pkgx/envs/nodejs.org.env" && -e "${PKGX_HOME:-$HOME/.pkgx}/nodejs.org/v*/bin/node" ]]; then
+      set -a
+      source "${XDG_CACHE_DIR:-$HOME/.cache}/pkgx/envs/nodejs.org.env"
+      exec "${PKGX_HOME:-$HOME/.pkgx}/nodejs.org/v*/bin/node" "$ARGS"
+    else
+      pkgx_resolve
+    fi
+  }
+  run
else
  cd "$(dirname "$0")"
  rm node && echo "uninstalled: nodejs.org" >&2
fi

Edit: this version of the stub is outdated, and doesn't match the actual one implemented. Please refer to the code instead.

Additional changes

Additionally, this PR also makes the following behavioural changes to PKGX:

  1. pkgx uninstall now also removes the env stub of the package
  2. pkgx install now checks for pkgx (instead of exec pkgx) in a script to consider it as installed by PKGX (because unsafe installations don't use exec pkgx)

Usage

To do an unsafe installation, there are two options:

  1. Use the --unsafe flag (e.g pkgx install --unsafe node)
  2. Use the PKGX_UNSAFE_INSTALL environmental variable.

In the presence of either, the --unsafe flag takes precedence.

Caveats

This approach doesn't come without any caveat. It's necessary to document them upon having such a functionality.
As per my knowledge, these are the known issues:

  1. If the cache dir doesn't exist (after installing the package), it fails.

Edit: it has been fixed

  1. If the envs need updating at a later point, that has to be done separately.
  2. If a certain dependency is removed, it doesn't properly check for that, and would error.
  3. These stubs aren't 100% managed by PKGX (unless you remove the env, when they'll be updated by PKGX)

Most (if not all) of them error correctly and offer (potentially) helpful error messages.

[1]
benchmarking node, the node.unsafe is newer one, and the node.safe is the normal one
benchmarking emacs, the emacs.unsafe is the newer one, and the emacs.safe is the normal one

Signed-off-by: Rajdeep Malakar <rajdeepm.dev@gmail.com>
Signed-off-by: Rajdeep Malakar <rajdeepm.dev@gmail.com>
@dosubot dosubot bot added the size:M This PR changes 30-99 lines, ignoring generated files. label May 11, 2024
Signed-off-by: Rajdeep Malakar <rajdeepm.dev@gmail.com>
Signed-off-by: Rajdeep Malakar <rajdeepm.dev@gmail.com>
@rustdevbtw
Copy link
Contributor Author

Here is another benchmark, comparing node.unsafe (installed through --unsafe), node.safe (normally installed), pkgx node, and the system-installed (in this case, installed from Arch repos) node:
benchmarking node

I believe (though I could be wrong) that the 1.1x diff is because of the shell overhead, and overhead of checking files.

@rustdevbtw
Copy link
Contributor Author

rustdevbtw commented May 12, 2024

Changed the mkdir -p (on the first run, or when the cache doesn't exist) to do that on $(dirname ~/.cache/pkgx/envs/gnu.org/emacs.env) so it creates the gnu.org dir also (the install also takes care of this, but if the cache is missing, it'll cause error, as mentioned in 1st caveat). It handles the first caveat.

src/modes/install.ts Outdated Show resolved Hide resolved
src/modes/install.ts Outdated Show resolved Hide resolved
src/modes/install.ts Outdated Show resolved Hide resolved
Comment on lines -68 to +98
const found = value.match(/^\s*exec pkgx \+([^ ]+)/)?.[1]
const found = value.match(/^\s*pkgx \+([^ ]+)/)?.[1]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might be dangerous, since it would catch a script with #!/usr/bin/env pkgx +python ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some other way of detecting both normal and unsafe packages? We can use special regex to match maybe both exec pkgx OR some other unsafe-specific (non-pkgx) match?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this instance. at least, you could search for ${pkgstr}.env. that should be unique to your shim.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, stat ~/.local/cache/pkgx/envs/${pkgstr}.env, and if it exists, and that has pkgx in it, consider it to be installed via PKGX?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if instead, unsafe installations contain a specific line (like PKGX_UNSAFE=1) and check that? Unlike safe (normal) installations, we have full control over how the scripts are gonna be, and there's no backwards compatibility requirement.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i meant that the script should contain the string ${pkgstr}.env, since they source it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that could be done. Probably with regex as well.

src/modes/uninstall.ts Outdated Show resolved Hide resolved
@@ -89,6 +91,9 @@ export default function(input: string[]): Args {
case 'update':
flags.update = true
break
case 'unsafe':
flags.unsafe = true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could simplify ENV handling by moving it here; i don't know if we do that otherwise (as with, say, verbosity or debug).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No other option uses ENV yet. So, it would have been overkill, probably. Maybe I can follow-up with a PR adding ENV options for other flags (PKGX_VERBOSITY etc.)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worth starting a discussion about, definitely. experience teaches us that there are sometimes unwritten design principles at play; though, in general, providing for either flag or env control allows a wider variety of use-cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so we should prioritise flags, right? So, if a flag is unset, only then it'll check the env. If that's fine, I'll do a PR for that after this one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think either being set means we do it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about PKGX_UNSAFE_INSTAL=0? It means false, so we shouldn't consider it to be set. And if it's this, we should check for the other.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think if either --unsafe or PKGX_UNSAFE_INSTALL != 0, then we do an unsafe install. PKGX_UNSAFE_INSTALL=0 should basically do nothing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean, the env should take priority?

Then, it needs to be like:

function is_unsafe(unsafe: boolean): boolean {
    const env = Deno.env.get("PKGX_UNSAFE_INSTALL");
    if (env) {
        // If present, it takes priority
        return parseInt(env) ? true : false;
    } else {
       // Otherwise, check flag
       return unsafe;
    }
}

@rustdevbtw
Copy link
Contributor Author

@jhheider It seems to be okay, but I'm not in PC rn (it's 1:02AM here lol). Can you try it now please, if possible?

@jhheider
Copy link
Contributor

@jhheider It seems to be okay, but I'm not in PC rn (it's 1:02AM here lol). Can you try it now please, if possible?

bugs in the script, but i can add a commit to fix them.

@jhheider
Copy link
Contributor

but the speed looks good:

pkgx hyperfine "node --version"
Benchmark 1: node --version
  Time (mean ± σ):      26.4 ms ±   2.1 ms    [User: 18.8 ms, System: 3.5 ms]
  Range (min … max):    25.0 ms …  46.3 ms    103 runs

  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.

@rustdevbtw
Copy link
Contributor Author

@jhheider It seems to be okay, but I'm not in PC rn (it's 1:02AM here lol). Can you try it now please, if possible?

bugs in the script, but i can add a commit to fix them.

Thanks. Sucks to be without laptop/PC, lol. Anyways, thanks again.

@rustdevbtw
Copy link
Contributor Author

but the speed looks good:

pkgx hyperfine "node --version"
Benchmark 1: node --version
  Time (mean ± σ):      26.4 ms ±   2.1 ms    [User: 18.8 ms, System: 3.5 ms]
  Range (min … max):    25.0 ms …  46.3 ms    103 runs

  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.

Is that right after doing the install, or after doing an initial run? It caches only at the first run, so subsequent runs should be faster than the first one. And the first one might interfere with the rest.

@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels May 14, 2024
@dosubot dosubot bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels May 14, 2024
src/modes/install.ts Outdated Show resolved Hide resolved
@jhheider
Copy link
Contributor

You mean, the env should take priority?

No, I mean if either is set, it should be on. Which is the way you have it, I believe. Setting the envvar to zero shouldn't do anything.

@rustdevbtw
Copy link
Contributor Author

You mean, the env should take priority?

No, I mean if either is set, it should be on. Which is the way you have it, I believe. Setting the envvar to zero shouldn't do anything.

By shouldn't do anything, do you mean it should ignore that? So, if that's set, regardless of its value, it should do an unsafe install?

@rustdevbtw
Copy link
Contributor Author

You mean, the env should take priority?

No, I mean if either is set, it should be on. Which is the way you have it, I believe. Setting the envvar to zero shouldn't do anything.

By shouldn't do anything, do you mean it should ignore that? So, if that's set, regardless of its value, it should do an unsafe install?

If yes, it gets simpler:

function is_unsafe(unsafe: boolean): boolean {
    return Deno.env.get("PKGX_UNSAFE_INSTALL") || unsafe;
}

@jhheider
Copy link
Contributor

So, if that's set, regardless of its value, it should do an unsafe install?

No. If it's non-zero, or the --unsafe flag is passed, it should do an unsafe install. If the envvar is zero, it has no effect one way or the other.

@rustdevbtw
Copy link
Contributor Author

rustdevbtw commented May 15, 2024 via email

@jhheider
Copy link
Contributor

jhheider commented May 15, 2024

The default behaviour (without any env or flag) is to install normally. So, if the env is set to 0, it should do that?

Exactly. Setting it 0 is the same as not setting it. So, it doesn't affect behavior.

If it's set to 0 and the flag is passed it's unsafe. If it's 0 and no flag, then safe. Set to 0 is a no-op.

@rustdevbtw
Copy link
Contributor Author

rustdevbtw commented May 15, 2024

The default behaviour (without any env or flag) is to install normally. So, if the env is set to 0, it should do that?

Exactly. Setting it 0 is the same as not setting it. So, it doesn't affect behavior.

If it's set to 0 and the flag is passed it's unsafe. If it's 0 and no flag, then safe. Set to 0 is a no-op.

So, this:

function is_unsafe(unsafe: boolean): boolean {
    const env = parseInt(Deno.env.get("PKGX_UNSAFE_INSTALL") || "0") ? true : false;
    return env || unsafe;
}

With this, there are six possible combinations:

  1. ENV doesn't exist, but Flag is used == unsafe.
  2. ENV doesn't exist, and Flag isn't used == normal.
  3. ENV exists and is 1, but Flag isn't used == unsafe.
  4. ENV exists and is 0, but Flag isn't used == normal.
  5. ENV exists and is 1, and Flag is used == unsafe.
  6. ENV exists and is 0, and Flag is used == unsafe.

@rustdevbtw
Copy link
Contributor Author

@jhheider implemented it, is it okay? If so, can you please test that the env works as expected? (It probably does, but still a test is a good idea)

@jhheider
Copy link
Contributor

right, i don't think that changed anything. your original implementation checked the truth value of the env || the flag. so, it should still be working correctly.

@rustdevbtw
Copy link
Contributor Author

So @jhheider anything else that's left? (Maybe check if it's by PKGX part? We can improve that)

Also @mxcl can you please share thoughts?

@jhheider
Copy link
Contributor

looks like good work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size:M This PR changes 30-99 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Can we get more scalable programs on PKGX? Programs installed by pkgx needs to be quicker
2 participants