Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Auto install bundle #9490

Closed
wants to merge 13 commits into from
Closed

Auto install bundle #9490

wants to merge 13 commits into from

Conversation

redmunds
Copy link
Contributor

@redmunds redmunds commented Oct 8, 2014

Automatically install extensions "bundled" with installer on startup. This feature relies on the installer putting zip files for "bundled" extensions into the [install-dir]/auto-install-extensions/ folder.

This is for #9233


Testing Matrix for auto-install-extensions folder

Should fail silently

  • is missing
  • is empty
  • contains file which has non-zip file extension
  • contains file which has non-extensions with zip file extension
  • contains extension that is already auto-installed
  • contains extension that is already manually installed
  • contains extension that has a newer version auto-installed
  • contains extension that has a newer version manually installed
  • contains extension that was already auto-installed, then manually removed

Should install extension

  • contains extension that is not yet installed (auto or manually)
  • contains extension that has an older version auto-installed
  • contains extension that has an older version manually installed

@peterflynn
Copy link
Member

I haven't looked at the code yet, but regarding those bullets:

  • The best time to run it might be right after ExtensionLoader.init(), when all the other extensions have loaded. Waiting until appReady seems unnecessary (and slower).
  • I think we really do still want it to be silent.

@redmunds
Copy link
Contributor Author

redmunds commented Oct 9, 2014

Ready for another review:

Installing extensions is working. Actually only tested a single extension. I haven't yet tested updating or extension already installed scenarios.

  • Code still executes in appReady() since Package module does not start to load extensionsManager node connection until appReady() anyway. And still need to wait until Package._getNodeConnectionDeferred() resolves.
  • I guess it doesn't need to use appReady() in brackets.js
  • Code looks for .zip files in [installation-dir]/bundles/ folder -- better name or location?

cc @dangoor @peterflynn

@redmunds redmunds changed the title [Review] Auto install bundle Auto install bundle Oct 10, 2014
AppInit.appReady(function () {
AppInit.appReady(function () {
// Check for updates
if (!params.get("skipUpdateCheck") && !brackets.inBrowser) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since an (eventual) in-browser version can't be updated client-side, wouldn't it be more performant to check for that first?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that does make sense. That code was not added for this feature, but I went ahead changed it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I noticed this code was already present, but I don't ever hurts to perform trivial optimizations like this one. :)

FileUtils = require("file/FileUtils"),
Strings = require("strings"),
StringUtils = require("utils/StringUtils"),
ThemeManager = require("view/ThemeManager");
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need that extra indent level.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! I removed the dependency that I increased the indentation for :)

bundles = [],
installZips = [],
updateZips = [],
deferred = new $.Deferred();
Copy link
Contributor

Choose a reason for hiding this comment

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

Code style: this would look a bit better like so:

var validatePromise,
    srcPath  = FileUtils.getNativeBracketsDirectoryPath(),
    dirPath  = srcPath.substr(0, srcPath.lastIndexOf("/")) + "/bundles/",
    deferred = new $.Deferred(),
    bundles     = [],
    updateZips  = [],
    installZips = [];

Copy link
Contributor

Choose a reason for hiding this comment

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

FWIW, I find the "Align It!" extension really handy for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, but I indented everything to same level.

@dangoor dangoor self-assigned this Oct 14, 2014
// Get list of extension bundles
var validatePromise,
srcPath = FileUtils.getNativeBracketsDirectoryPath(),
dirPath = srcPath.substr(0, srcPath.lastIndexOf("/")) + "/bundles/",
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: might be nicer to say `dirPath = FileUtils.getDirectoryPath(FileUtils.getNativeBracketsDirectoryPath()) + "bundles/";

Copy link
Member

Choose a reason for hiding this comment

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

For the folder name, something a little more self-explanatory might be good... e.g. auto-install-extensions. ("bundle" is a term we haven't really used for extensions before, anyway).

Also might be good to move the folder name into a constant...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dangoor It depends on where we want to put the folder. FileUtils.getDirectoryPath(FileUtils.getNativeBracketsDirectoryPath()) returns [install-path]/www so your suggestion would put new folder inside the /www folder (i.e. [install-path]/www/bundles). But, I wanted it to be a sibling to /www (i.e. [install-path]/bundles).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@peterflynn I started with "bundles" just to get the code going, but I agree that it's not quite right. To me, the "bundle" is the "installer plus the extensions" (not just the extensions). Unless I hear any better suggestions, I'll change it to "auto-install-extensions" and put it in a constant.

Copy link
Contributor

Choose a reason for hiding this comment

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

Not so. FileUtils.getDirectoryPath does basically what your code does: it chops off until the last slash. Since getNativeBracketsDirectoryPath is old-style and doesn't include the trailing slash, the result doesn't go into www.

> FileUtils.getDirectoryPath(FileUtils.getNativeBracketsDirectoryPath())
"/Applications/Brackets.app/Contents/dev/"

I agree with Peter's points also about auto-install-extensions and using a constant.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dangoor Ah, I missed that point. Done.

@ingorichter
Copy link
Contributor

I think we should add a couple of tests for this new feature.

@ingorichter
Copy link
Contributor

So, the folder will be named bundles? I need to update the E4B build job.

@dangoor
Copy link
Contributor

dangoor commented Oct 14, 2014

Tests would be good, but I'm concerned with the difficulty in writing tests for this particular bit of code. _autoInstallBundles could likely be broken up in a way that allows testing of the logic independent of integration with the rest of the system.

@redmunds
Copy link
Contributor Author

@ingorichter The latest idea is to call the folder "auto-install-extensions". Any other suggestions?

@marcelgerber
Copy link
Contributor

Maybe "preinstalled-extensions" (or even "bundled-extensions")?

@redmunds
Copy link
Contributor Author

@marcelgerber Thanks for the suggestions. This code is doing the installing, so "preinstalled" doesn't sound right to me. I think "auto-installed-extensions" is a little more descriptive than "bundled-extensions".

@redmunds
Copy link
Contributor Author

Ready for another review. All comments have been addressed except tests -- let me know if you have any thoughts about what could and should be tested.

@ingorichter
Copy link
Contributor

auto-installed-extensions feels more appropriate to me.

@redmunds
Copy link
Contributor Author

Oops, the actual folder name is slightly different: auto-install-extensions.

@dangoor
Copy link
Contributor

dangoor commented Oct 15, 2014

It looks like this does not yet handle the final two points in the story:

  • I modified the installed package.json of an autoinstalled extension to have a higher version number. The extension was re-installed on launch.
  • I removed the autoinstalled extension via the Extension Manager and it was reinstalled on launch.

@redmunds
Copy link
Contributor Author

@dangoor Ready for another review

@redmunds
Copy link
Contributor Author

I added a testing matrix to description. Let me know when this is ready and I'll squash the commits.

// Listen to extension load and loadFailed events
$(ExtensionLoader)
.on("load", _handleExtensionLoad)
.on("loadFailed", _handleExtensionLoad);

// Public exports
exports.autoInstallBundles = autoInstallBundles;
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 we should call this private API. The only caller of this should be Brackets itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dangoor What if ExtensionManager listens for AppInit.appReady() (instead of calling it from brackets.js) ? This way it does not need to be exported at all.

Copy link
Contributor

Choose a reason for hiding this comment

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

That would be fine, too.

@dangoor
Copy link
Contributor

dangoor commented Oct 16, 2014

Mark autoInstallBundles private and I think this is ready to go into testing.

A couple of comments about unit testing, because I think that quick to run tests are a great way to specify the details of the expected behavior.

If we had versions of our Async functions that would return the results/rejections in arrays, we could gather up all of the package info and then run that through a nice, easy-to-test, synchronous, pure function that contains all of the logic around checking versions, which ones need install, which ones need update and just returns the install/update instructions. I think that's generally a nicer factoring anyhow (gather up the data, work through the data, process side effects).

Alternatively, if we had a good way to mock modules (ideally like rewire), we could test the whole routine by mocking out just the bits we need. I think it should be possible to do what rewire does in our test context and I'll play with that sometime.

At the moment, we have neither of those infrastructure pieces to do that testing...

@redmunds
Copy link
Contributor Author

This PR is replaced by #9573. Closing.

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

Successfully merging this pull request may close these issues.

6 participants