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

Execute post-install/post-update scripts of dependencies #1193

Closed
ezzatron opened this issue Oct 9, 2012 · 53 comments
Closed

Execute post-install/post-update scripts of dependencies #1193

ezzatron opened this issue Oct 9, 2012 · 53 comments

Comments

@ezzatron
Copy link

ezzatron commented Oct 9, 2012

Is there a valid reason why scripts are not executed for dependencies? I'm sure this has been discussed before, but I couldn't find an issue here.

I have a use case. I have written a component that allows NPM packages to be installed/updated at the same time as Composer's dependencies by using the scripts system.

The component works great for end projects, but obviously cannot be used for dependencies currently, which is where it would actually be useful.

Even if it's not something that will be implemented, it would be nice to know why it's not possible or desirable.

@Seldaek
Copy link
Member

Seldaek commented Oct 9, 2012

There are moderate security concerns with doing that, but we just did not do it because we were not sure whether it was a good idea. We named the events in a way that we can add a post-install script that would be executed on the package when it is actually installed, so it should be no problem to add without breaking anything.

That said, I don't think it's a huge drawback that users of your lib must add the scripts to their own composer.json.. It is one extra step but at least it makes it very obvious what is executed, and it's easy to remain in control as a user, so I would still favor the status quo.

@ezzatron
Copy link
Author

ezzatron commented Oct 9, 2012

That's fair enough. In this circumstance, my workplace has around 40 individual Composer packages, so the 'one' extra step is immediately multiplied into lots of copy-paste/redundant tedium (not all of them would use this feature, but still, there's a few).

Perhaps in the future, like you suggested, an event that fires when a package is installed would be a useful addition.

@Seldaek
Copy link
Member

Seldaek commented Oct 10, 2012

Do you have concrete examples you can share of what you actually do with those scripts?

@ezzatron
Copy link
Author

The intention is to have a composer package that handles compilation of CoffeeScript and JavaScript libraries into production-ready minified javascript versions. We call this package build-javascript, and it can be brought into any of our other packages as a dev dependency when that package has some javascript component to it.

In order to do this, build-javascript needs the CoffeeScript compiler and uglify.js which are available through NPM. We actually have the system working fine right now, but we have to commit the node_modules directory to the build-javascript repo, which is obviously not ideal. That is the problem I was trying to solve by creating a bridge between Composer and NPM.

In a more general sense, it's quite common for PHP devs to use some client-side scripting in their projects. Allowing Composer packages to bring in their own NPM dependencies seems like a good idea to me, whether it be baked into Composer or possible through a post-install script type arrangement.

@Seldaek
Copy link
Member

Seldaek commented Oct 11, 2012

I see, but I'd rather try to support NPM in time than allow hacks to appear, because then we need to support hacks forever. In the meantime workarounds can usually be found, even though they're a bit more painful to use than the hacks we avoid, but that means if a good solution pops up people will have an incentive to migrate away from the workarounds.

@johnkary
Copy link
Contributor

@ezzatron Quickly looking at your bridge app it looks like it's basically just a PHP wrapper around executing npm update and npm shrinkwrap. I have a pending PR #1186 that allows executing CLI commands via Composer script events.

Would my PR's functionality fulfill all the reasons your composer-npm-bridge library exists? If not, what additional features would Composer's CLI execution functionality need to fulfill your use case?

@ezzatron
Copy link
Author

I think your PR would be capable of doing what my bridge component currently does, but what I'm really after is for it to work for dependencies and not just root projects. So unless your PR addresses that aspect of Composer's scripts system, it doesn't solve the use case I mentioned in my previous comment.

Having said that, if scripts were executed for dependencies too, your PR would probably then do everything I need.

@nicodmf
Copy link

nicodmf commented Nov 19, 2012

I need too that composer send child commands after the installation process, but after thinking about that, i demand me what script should be launch and in what order. It exists 4 phases for 3 commands :

install update uninstall
pre-install-cmd pre-update-cmd pre-package-uninstall
pre-package-install pre-package-update post-package-uninstall
post-install-cmd post-update-cmd
post-package-install post-package-update

Should the pre-install-cmd be launch before the parent pre-install-cmd or after, same question for update and unintall commands. In my mind the order could be one of theres

parent pre
child1 pre
child2 pre
parent post
child1 post
child2 post
child1 pre
child2 pre
parent pre
child1 post
child2 post
parent post
child1 pre
child2 pre
parent pre
parent post
child2 post
child1 post
parent pre
child1 pre
child2 pre
child2 post
child1 post
parent post
child1 pre
child2 pre
parent pre
parent post
child1 post
child2 post

Note: I volontary omit cases ( parent_pre>parent_post>child_pre>child_post and child_pre>child_post>parent_pre>parent_post)

Personally i prefer the third which is more semantically exact (first arrived first out, and a dependency exists before the dependents packages)

The fith is just a variation on the requires, easiest to implement mainly if the requires are accessibles and acceded as a tree and not as a flat list.

In this case, a tree access, for each event, the designer should defined if a command should be launch one or each time the require appears.

@hinikato
Copy link

I have received the problem that @ezzatron described also and I agree with him: I have expected the all event handlers to be called for dependent packages however they were not called.

The use case: there is https://github.com/myak/framework/blob/master/composer.json file and there are event handlers described there, I expect for the following composer.json:

{
    "require": {
        "myak/framework": "0.*"
    },
    "minimum-stability": "dev"
} 

The \Myak\Deploy\Deployer::postInstall() or postUpdate() to be called.

The logic that Composer can use can be simple: after installation of the package all event handlers for this package must be called. Every package just need call of its event handlers, no need to worry about parent/child event handlers because it can lead to very complex logic with ambiguity.

The reason why this have to be done: if we have 100 dependent packages and every package has event handlers defined in its composer.json file, developer/user need to find all these files, copy and paste the "scripts" section into the root composer.json file. Obviously it is not what is expected.

@Seldaek
Copy link
Member

Seldaek commented Feb 28, 2013

@hinikato I'm really against this, because it removes the control from the users and places it in the hands of package authors. It's a good thing for metadata like autoloading because that makes things easier for everyone, but in this case, executing stuff is something that is best done by the root package, where you can control what is executed and in what order.

@hinikato
Copy link

@Seldaek, I understand, it makes sense, if consider this logic from this point of view - due some bug in some event handler all packages can be in not working state.

@mwjames
Copy link

mwjames commented Dec 21, 2013

@Seldaek, are you saying that supporting of running scripts in dependencies will not be supported now or in future?

We are preparing to use Composer in connection with MediaWiki/SemanticMediaWiki and while we can educate people to update the MediaWiki composer.json with something like:

php composer.phar require mediawiki/semantic-media-wiki @dev

I would not expect a user to search the Semantic MediaWiki composer.json file, look for scripts, and make him/her to copy this part into the MediaWiki related composer.json file (root composer file). If this is required from a "normal" user then it defies the idea of handling dependencies without deeper knowledge of a related package.

"scripts": {
    "post-install-cmd": "SMW\\Maintenance\\ComposerInstaller::postInstallEvent"
}

We do an effort to make the migration to use Composer in connection with MW/SMW as easy as possible but making a user understand to edit a json file by hand is certainly rather difficult.

@nicodmf
Copy link

nicodmf commented Jan 11, 2014

@Seldaek "removes the control from the users and places it in the hands of package authors." : maybe a composer_key could resolve this problem, a key like :

authorized_sub_commands : without_confirmation, true or false

@Seldaek
Copy link
Member

Seldaek commented Jan 12, 2014

Yes maybe we could add this with an optional flag. I'm still not sure it's very needed. You could have a custom installer allowing hooks in packages it installs, or maybe even with a plugin if we add the given hook. I feel it'd be best than to add this in composer itself.

@leafpeak
Copy link

I ran into this issue as well. I'd like to see it...Its useful for github enterprise users.

A key like

"authorize_vendor_scripts":true

would work?

@motin
Copy link

motin commented May 26, 2014

+1 for an ability to enable this. Npm allows it afaik.

@ezzatron
Copy link
Author

As the ticket creator, I can say with confidence that I no longer need this feature. Others who find this thread should probably consider whether the Composer plugin system fits their needs instead. For instance, I managed to implement the use case I originally stated in the ticket as a plugin (composer-npm-bridge).

@mcd-php
Copy link

mcd-php commented Mar 5, 2015

+1 for this.

I need to patch other packages from dependency packages - since I have too weak bargaining power to impose my patches upon upstream framework vendor.

my/FooERP depends on yiisoft/yii2 and my/ApacheCassandraForYii2, I need to patch yiisoft/yii2 from my/ApacheCassandraForYii2 at the moment it's being installed as dependency for my/FooERP.

@Seldaek
Copy link
Member

Seldaek commented Mar 5, 2015

@mcd-php you shouldn't need bargaining power IMO if you are doing sensible things you should be able to get it in, unless upstream is crazy but then maybe it's time to work with another project.

And anyway hot-patching code on install really sounds like a bad idea. You could tell users of your lib that they should require your fork of yii2 if they want to work with your package, but IMO that's a poor proposal as well.. imagine if everyone relied on their own forks the whole ecosystem falls apart.

@younishd
Copy link

@Seldaek

@sminnee
Copy link
Contributor

sminnee commented Jun 19, 2015

Just a quick note about the reference I just posted: one use-case for this feature is conditional requirements.

@spekary
Copy link

spekary commented Jul 1, 2015

@Seldaek

So, here is our use case. We would like to use composer to be able to install our framework, and individual packages that are plugins to the framework. As part of the installation process, we want to copy specific stub files that we just installed out of the vendor directory and into a user editable space so that the user can edit them and not worry about composer stomping on those edits on the next update. We are trying to use scripts to do the copy of those files.

With the current architecture, I need to tell these users to make certain 'scripts' entries to the composer.json file. HOWEVER, if there is already a 'scripts' entry there because of other dependencies, we need to tell them to MERGE the entries into what is already there. If they remove our framework, they will have to un-merge by hand.

Now, our users are not all experts. I would like to avoid the requirement of needing to know how to hand-edit a .json file before being able to use our framework. Explaining how to do this hand-editing, and to be careful of every little comma and bracket can lead to quite a bit of frustration that will be a barrier to entry for our beginning developers. With the current architecture, I can't tell them to just execute 'composer require'.

I guess I don't understand your point about losing user control. If I require a package in my top level composer.json file, I want that package to install. If that package fires off some scripts during installation, as a user of that package, I WANT that. I could easily inspect that package's composer.json file to see what it is trying to do if something goes wrong. The package creator can communicate in its documentation that it is firing off scripts at install and update time. Composer could notify the user during the composer execution process by outputing "Executing xyz script from package ABC" as part of the messages it sends out. Composer could have some options to not execute package scripts, or even have an interactive option to ask for confirmation on each script it finds. All of these would give the user sufficient control in the very unlikely event that the user would want that.

If you still don't agree, then perhaps at a minimum have the composer require and composer remove operations look for scripts entries in the required packages and merge and underage those entries to avoid a bunch of user error in the manipulation of the composer.json file.

You could have a custom installer allowing hooks in packages it installs, or maybe even with a plugin if we add the given hook. I feel it'd be best than to add this in composer itself.

Don't both of these options require a bunch of work by the developer, and the user has to create special entries in the top-level composer.json file? And these packages are then allowed to fire custom scripts on their own. I don't see how doing more work is better, when the end result is basically the same thing, that a package that was installed is firing a script at install time. I don't see how calling it a 'composer-plugin' means that the user has more control than the scenario that any required package could fire off scripts.

@younishd
Copy link

younishd commented Jul 1, 2015

@spekary Thank you!

If I require a package in my top level composer.json file, I want that package to install. If that package fires off some scripts during installation, as a user of that package, I WANT that.

I totally agree.
There should at least be some kind of option like --run-dep-scripts to explicitly allow a dependency to fire off some custom scripts during installation.

If at the end of the day I still need to edit some JSON files by hand to get my dependencies to work then why bother with composer in the first place?

@stof
Copy link
Contributor

stof commented Jul 1, 2015

The point is that use cases described here are kind of abusing what composer is for. It is not a boostrapping tool for framework-based projects.
thus, your script would have to run for the initial project creation, but not for any installation of the package (installing a project on another machine later because you have several devs, or updating the packages), because it would then overwrite these files again, loosing the benefit you describe here. A tool bootstrapping a project based on your framework makes much more sense IMO.

@spekary
Copy link

spekary commented Jul 1, 2015

because it would then overwrite these files again

The script simply checks to see if the file is there, and if it is already there, it will not over-write it. Its not that difficult.

So, perhaps this should become an open issue debate on what IS composer. The dev team sometimes calls it a dependency manager, and sometimes an installer. Whether the dev team knows it or not, Composer has become the defacto way to install PHP libraries and even whole frameworks for people who want to use those things to develop websites. The PHP developer community expects all PHP libraries and frameworks to be Composer installable. As such, there is a certain amount of responsibility the dev team has to make it easy to use for novice PHP developers. Admittedly, Composer has a little bit become a victim of its own success.

I am not asking for Composer to be a complete solution for all bootstrapping. However, I AM asking that package developers be given the ability to configure their own composer.json files in such a way that composer require package is all that the user needs to do to install a framework, or a framework plugin, or a library, or whatever.

Composer is 99% there. Script launching is the missing piece. The issue has come up over and over, I would think the message is pretty clear.

@Seldaek gave some principles for why dependencies should not launch scripts. Mainly, he said users would lose control of the installation process. I assume he means that since a script isn't explicitly called out in the top-level composer.json file, it is therefore hidden and therefore there is some loss of user control. I think there are lots of ways of mitigating this and I gave some. However, I think he is trying to protect users from something they don't want protection from.

I think the primary problem is a misunderstanding of your audience. There seems to be an assumption that all users of composer are accomplished programmers and .json editors. I would say most composer users are at the level of struggling to just get composer installed, and they follow a set of commands or instructions to use composer to get some PHP thing installed. They do NOT want to know the inner workings and they would like to avoid editing a .json file as much as possible. The composer require command is a great tool for them.

The principle I would recommend going by is "Give the people with the problem the ability to solve their own problem." The dev team tried to do that with plugins and custom installers, but unless I am mistaken (and I easily could be, I don't totally understand how a user configures a composer.json file to use a package's custom installer), there is no way for package developers that need some extra installation steps to tell users to just composer require in order to install.

@saitho
Copy link

saitho commented Jan 3, 2017

Also +1. About security concerns and backwards compatibility: wouldn't it be enough if the feature has to be explicitly enabled in the root composer.json file?

@ghost
Copy link

ghost commented Jun 14, 2017

I'm ashamed to be looking for the same thing. Please forget I was here ;)

@derhecht
Copy link

Also have this need :(

@ghost
Copy link

ghost commented Jul 17, 2017

Best option is to have people use "composer create-project" Then you get git clone and composer install which can do the installation process for them. That was the best alternative in my case.

@mindplay-dk
Copy link
Contributor

What about a package such as this one that needs to set permissions of bundled binaries after installation?

I tried this and it doesn't work.

The create-project work-around doesn't make sense for this.

I could set the type as composer-plugin and make it execute the install-script that way, but that seems really hacky.

I don't understand the security concerns around this? I mean, I can just set my package type as composer-plugin and execute arbitrary code anyway, how is it any more of a concern to provide a simple means of executing a script post-install of a package?

@mindplay-dk
Copy link
Contributor

Another hacky work-around would be to use composer-locator and the bin option to make Composer automatically set the permissions at installation time - to do that, I would need to rename the binaries and put them in a single folder, which doesn't exactly make sense either.

Bottom line is there are plenty of hacky ways to do this, so why not provide an official way to do it?

If security is the only reason, we have a slew of features already that are dangeous anyway... don't we?

@Seldaek
Copy link
Member

Seldaek commented Jul 25, 2017

I just don't see the problem at all for this package.. Git supports setting permissions in the file metadata, which will propagate fine on unix systems and is kinda ignored on windows but there it's irrelevant anyway. Why not just set the binaries to be executable in git and be done with it?

@mindplay-dk
Copy link
Contributor

Why not just set the binaries to be executable in git and be done with it?

I had no idea that was possible. I'll try.

@Seldaek
Copy link
Member

Seldaek commented Jul 25, 2017

git update-index --chmod=+x filename if you're on windows, on other OSs you can just do a chmod then git add.

@mindplay-dk
Copy link
Contributor

That works, thanks! Sorry for taking up your time, but I tried everything I could find, I just had no idea this was even possible with Git in the first place.

@Seldaek
Copy link
Member

Seldaek commented Jul 25, 2017

I don't mind if someone learned something ;)

@ghost
Copy link

ghost commented Feb 14, 2018

As this has been a requested feature for 5 years now, with people still requesting it (myself now included) can this not be implemented as a composer.json config option at least?

My use case is migrating a database. We have packages that are grouped together in a meta composer repo. We then install this single repository onto the API infrastructure. I was hoping to have composer auto-run the package post install scripts, which would run the relevant database migrations for each required package.

If I can't do this, has anyone any suggestions how to do it? I really haven't the time to learn the composer plugin architecture to achieve something I thought possible already (and therefore haven't accounted sprint time for).

A simple composer.json feature switch (defaulting to false) like "run-dep-install-scripts": true would be a godsend. An additional security measure would be to add package vendors to trust?

"dep-install-scripts": {
    "run": true,
    "trust": ["phpunit", "illuminate"]
}

Thoughts?

@ghost
Copy link

ghost commented Feb 15, 2018

Contact me @johnrobertporter. As Composer has good reasons not to do this. It's probably not that hard to provide a plugin for this with the config you describe. Merge plugin already does similar stuff. Althouh it might require a re-run on initial install of the plugin. Unless you put it globally. verbruggenalex-at-gmail-dot-com.

@tonglil
Copy link

tonglil commented Jul 12, 2018

Don't execute dependency code automatically, as then you'll run into this:
eslint/eslint-scope#39
https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes

@kurtfoster
Copy link

The suggestion from @johnrobertporter is exactly what I need. We have packages that we're writing ourselves that are used across multiple projects, it would be great to be able to whitelist them in this way, including their scripts.

@uphlewis
Copy link

uphlewis commented Oct 5, 2018

Don't execute dependency code automatically, as then you'll run into this:
eslint/eslint-scope#39
https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes

Blocking dependency update event scripts wouldn't help prevent that. A compromised package could just as easily run malicious code at runtime.

@stof
Copy link
Contributor

stof commented Oct 5, 2018

@uphlewis but you can have a chance to review the install code before the runtime. For code running at installation time, it runs before you could look at it.

@nolanpro
Copy link

nolanpro commented Jan 3, 2019

@kurtfoster @stof Don't know about you guys but I never look at source code after I composer require but before running the application if the code is from a reputable source. If anything, I would review it in github before running composer require.

I feel like running dependency scripts would be extremely helpful without introducing much more risk then what's already there when installing a 3rd party dependency.

@b-hayes
Copy link

b-hayes commented Jun 13, 2019

Why not a bin export? "bin" : ["bin/mypackage-setup"]
Your users do not need to edit any json files to add scripts just run a simple command after they require.

if you have a freamworky system with a lot of other component packages then you can just have one bin from your framework detect and setup other components.

Would be so easy to also make your setup command edit the users composer.json for them and add a scripts entry so it happens for every install/update command they do in future.

For npm dependencies can use npm-bridge but you can just as easily commit your node_modules folder instead. Since bridge will duplicate all the npm dependencies in each vendore/package/node_modules folder anyway then why not make it easy on yourself and also prevent users form breaking your package with npm updates.

@NicoHaase
Copy link
Contributor

@b-hayes bin export won't help to automatically run such a script after the installation, regardless of any framework or anything else, and that's what this feature request is about: define a way to run a script defined by a dependency without any further interaction

@b-hayes
Copy link

b-hayes commented Jun 25, 2019

@NicoHaase yeah I know I. But there is no way to automatically run scripts and its probably not coming anytime soon, or ever. So just offering a simple work around. It's only one command line after they run composer require. Not very hard for a user to do.

@werbfred
Copy link

I was looking for the same feature and made a composer library which is implementing the proposed solution from the 14 Feb 2018.

In order to avoid security breaches, only PackageEvents raised by a package will be forwarded to scripts matching the event in its package.

Pushed code here : https://github.com/mathias-meyer/composer-package-updater

I'm about to have it handled by packagist as well.

Mathias

@werbfred
Copy link

werbfred commented Nov 27, 2019

I'm about to have it handled by packagist as well.

Now available in packagist under werbfred/composer-package-updater

@totten
Copy link

totten commented Sep 13, 2020

Well, I did something very similar to allow dependencies to perform post-install compilation tasks. The permission model was initially pretty flat (eg on/off switch - with an interactive prompt to get permission on first run), but it looked like the "whitelist" approach had a lot of buy-in here, so I switched to a whitelist. Currently, it's tagged as v0.3.

In a library package (upstream POV), the composer.json declares an extra.compile blurb. It may include shell commands or PHP callbacks. Here's an example library from the test-suite which compiles SCSS:

{
  "name": "test/gnocchi",
  "require": {
    "civicrm/composer-compile-plugin": "@dev",
    "scssphp/scssphp": "1.2.0",
    "padaliyajay/php-autoprefixer": "~1.2"
  },
  "autoload": {"psr-4": {"ScssExample\\": "src"}},
  "extra": {
    "compile": [{"php-method": "\\ScssExample\\ScssExample::make"}]
  }
}

During installation on the root-package (downstream POV), composer will finish its normal work (downloading, dump-autoload, etc) and then run the compilation tasks. It prompts for permission first:

Screenshot

More details at https://github.com/civicrm/composer-compile-plugin

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

No branches or pull requests