Skip to content

After install scripts (#1904) #1908

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

Closed
wants to merge 12 commits into from
Closed

After install scripts (#1904) #1908

wants to merge 12 commits into from

Conversation

thosakwe
Copy link

This is a working implementation of the changes proposed in #1904.

I've added tests for parsing the necessary value (an after_install field in the pubspec), but I'm not so sure of how to test the functionality, other than physically running it myself.

To clarify, the behavior verifiably works when run by a human, but I'm not so sure how to run it via computer.

One other thing I'd like to propose is an additional check upon running pub lish that throws an error if a script specified the pubspec does not actually exist on disk (to prevent breaks on end-user machines).

I'm willing to fix anything that needs to be fixed to land this PR. I really feel like something so simple as support for post-install scripts could go a long way to growing the Dart ecosystem.

@natebosch natebosch requested a review from nex3 May 23, 2018 21:50
@nex3
Copy link
Member

nex3 commented May 24, 2018

I won't have time to review this any time soon.

@nex3 nex3 removed their request for review May 24, 2018 15:44
@thosakwe
Copy link
Author

No prob... Anything I can do in the meantime to make the future review easier?

Copy link
Member

@kevmoo kevmoo left a comment

Choose a reason for hiding this comment

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

I'm excited to see interest here, but we'd have to do a substantial security review before we could add such a feature. I'll chat more with @nex3 when we're both in the office...

@stepancar
Copy link

@kevmoo any progress on this PR?

@kevmoo
Copy link
Member

kevmoo commented Jun 14, 2018

Still waiting on @nex3 to get back in the Office.

Again, this is a tough one. It's not about the code quality or understanding the use case – it's about security concerns.

@thosakwe
Copy link
Author

Yeah, I can certainly understand that. If something like a post install hook could be used to do some sneaky business on users' computers, that certainly wouldn't be good.

I included an option for users to preview the source of all scripts that a package is requesting to run, so that could at least be a start. Also, the fact that after_install can only execute Dart scripts could potentially close at least a few holes.

@nex3
Copy link
Member

nex3 commented Jun 14, 2018

I don't have the time budgeted to review this, nor am I likely to any time soon.

Copy link
Contributor

@jakemac53 jakemac53 left a comment

Choose a reason for hiding this comment

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

Did a quick review just to get some conversation started here. I think this is great to get conversation started, even if we can't actually land it until Dart 2 settles down a little bit.

/// This is `true` if any of the following is true:
/// * The cache contains no entry for the [path].
/// * The file at [path] was modified after the timestamp in the cache.
Future<bool> isOutdated(String path) async {
Copy link
Contributor

Choose a reason for hiding this comment

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

Note that only checking the timestamp here is not a full solution. There could be arbitrary imports included which would also technically need to be checked which would be a lot more cumbersome.

I think we should re-think a little bit how we determine when to run "after_install" scripts.

For instance, one really awesome use case for this feature would be the ability to provide automatic upgrade scripts with a package. These would have to be tied to specific releases - and when upgrading from one release to another it should run all of the update scripts for all intermediate releases in order.

if (!await mergedCache.isOutdated(absolutePath)) continue;

if (!await scriptFile.exists()) {
if (pkg == root)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit, use block syntax if it can't fit on one line

// Never trust any third-party scripts. Show a prompt before running scripts.
//
// The user can opt to print the contents of the script.
YesNoPrintResponse response = YesNoPrintResponse.yes;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: omit the type on the left (just use var)

'\npackage:$package wants to run the script "$absolutePath".';

do {
response = await confirmOrPrint(message);
Copy link
Contributor

Choose a reason for hiding this comment

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

We would need to figure out how to handle this on CI systems like travis - or within editors like VsCode/Intellij

Copy link
Author

Choose a reason for hiding this comment

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

That's a valid concern, and something I didn't consider at all. At least for Travis, in theory a command-line arg or env variable could be used to auto-deny (or maybe auto-allow) every script?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes - I think the default behavior would need to be to auto-deny, with a flag or environment variable to auto-allow.

Copy link
Member

Choose a reason for hiding this comment

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

should we consider being able to whitelist scripts from certain packages?

Copy link
Contributor

Choose a reason for hiding this comment

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

should we consider being able to whitelist scripts from certain packages?

I like that idea


// We need to change into the package's directory to run its scripts.
var currentDir = Directory.current;
Directory.current = pkg.dir;
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't need to do this (and its probably not what many scripts would want, they may want to look at the current package and would expect that to be the current directory).

We definitely dont want to run inside the package cache like this would do, as touching anything in there is forbidden.

Copy link
Author

Choose a reason for hiding this comment

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

Sounds good, more than likely could be removed

Isolate.spawnUri(p.toUri(absolutePath), [], null,
onExit: onExit.sendPort,
onError: onError.sendPort,
automaticPackageResolution: true);
Copy link
Contributor

Choose a reason for hiding this comment

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

We should use the packageConfig argument and pass in the config from the current isolate, instead of automaticPackageResolution argument, similar to pub run behavior. This likely gets around the issue that was causing you to change directories to the other package?

Copy link
Author

Choose a reason for hiding this comment

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

That's probably the solution I was looking for, haha. Is there a way to specify a working directory for an isolate, though? In my head, the scripts would execute in the folder for the specific version that package, so that things like native extensions could be built to the correct paths.

Otherwise, though, people publishing scripts could still just Isolate.resolvePackageUri, probably

Copy link
Contributor

Choose a reason for hiding this comment

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

In my head, the scripts would execute in the folder for the specific version that package, so that things like native extensions could be built to the correct paths.

We really don't want to give access to the actual package cache. That should only contain the exact sources as they exist on pub because it is shared between all projects on your machine. The output of the after_install script could be different depending on the version solve of any given project. Therefore each individual project should have to re-run any after_install scripts and should generally write the results to the .dart_tool/<package-name> directory (by convention).

Copy link
Author

Choose a reason for hiding this comment

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

Okay, that's fair, and makes a lot of sense.

One more question: what can end-users do to depend on sources built to .dart_tool? By passing a custom package config? I'm not 100% sure how build does it.

Copy link
Contributor

Choose a reason for hiding this comment

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

That is actually a really good question haha - basically the build system knows how to surface those files via the serve command and within the build and treat it as an overlay file system. That isn't a general solution for all of .dart_tool though, its very specific to the .dart_tool/build/generated directory and the exact files that it knows it wrote there.

We would have to come up with some other solution here for pub, possibly writing custom paths to the .packages file that point to .dart_tool and copying the original package + generated sources there.

@thosakwe
Copy link
Author

thosakwe commented Jun 29, 2018

I've put more thought into this, and in the long run, I'm actually not so sure that after_install scripts will really be worth the potential security risks they introduce to Pub.

The main use case I had in mind for this functionality was distribution of native extensions; however, I've been able to get an early version of said functionality use existing infrastructure in build_native:
https://github.com/thosakwe/build_native

So, the merits of after_install scripts might actually be... Nothing?

edit: today, yet another NPM package with thousands of dependents was breached. It might not be possible to securely implement this at all.

@mit-mit
Copy link
Member

mit-mit commented Feb 20, 2019

Closing as it's my understanting we have decided not to move ahead with this.

@mit-mit mit-mit closed this Feb 20, 2019
@thosakwe
Copy link
Author

thosakwe commented Oct 7, 2019

If something like this is ever added to Pub, it occurred to me that instead of packages you depend having permission to run scripts, it would make more sense if your package could run hooks on get and upgrade, etc.

For example, you could create a .dart_tool/pub/hooks/post-get:

#!/usr/bin/env bash

# Build dependencies
pub run build_runner build --delete-conflicting-outputs

# Run some tool that compiles FFI libraries
pub run some_tool compile

That being said, I'm not sure if this would be useful to the majority of people using Pub, and it's already easy enough to just create a script that invokes pub get, rather than the other way around.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants