Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Generate module cache during install #200

Merged
merged 36 commits into from
Oct 15, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f591c6a
Log warning when installing a bundled package
kevinsawicki Sep 30, 2014
69c4d0a
Use hasOwnProperty
kevinsawicki Sep 30, 2014
dba32c5
Remove unused variable
kevinsawicki Sep 30, 2014
b020b43
Always use default lint task
kevinsawicki Oct 1, 2014
caeb410
Install latest compatible version
kevinsawicki Oct 1, 2014
36ec85f
Add install spec for no compatible versions
kevinsawicki Oct 1, 2014
6c9f469
Add uninstall instructions
kevinsawicki Oct 1, 2014
cb416bc
Add dedupe modules command with outline
kevinsawicki Oct 1, 2014
627d530
:lipstick: Remove extra newline
kevinsawicki Oct 1, 2014
5790b2b
Add initial dedupe modules implementation
kevinsawicki Oct 1, 2014
7a4d8f1
Only move folders that exist
kevinsawicki Oct 1, 2014
36a2c4c
Catch errors moving packages
kevinsawicki Oct 1, 2014
fc5cce1
Forward dedupe error to root callback
kevinsawicki Oct 1, 2014
fdad9b7
Ignore dependencies in malformed packages
kevinsawicki Oct 1, 2014
db83846
Keep moving packages even after first error
kevinsawicki Oct 1, 2014
fc51286
:lipstick:
kevinsawicki Oct 1, 2014
6a25657
:memo:
kevinsawicki Oct 2, 2014
5f7d8bd
Remove unused variable
kevinsawicki Oct 2, 2014
b6ca70f
Mention using bundled version
kevinsawicki Oct 2, 2014
bad30ff
Disable streaming since no longer debugging
kevinsawicki Oct 2, 2014
e4bdb1e
Log out deduped module names
kevinsawicki Oct 2, 2014
aeb2dee
Moved instead of deduped
kevinsawicki Oct 2, 2014
f77eea0
Build module cache after install
kevinsawicki Oct 8, 2014
b45f8ec
Remove dedupe package modules command
kevinsawicki Oct 8, 2014
ff9714a
Ignore any cache building errors
kevinsawicki Oct 8, 2014
4804acc
Use local getResourcePath method
kevinsawicki Oct 8, 2014
aa0cba7
Call back explicitly with null
kevinsawicki Oct 8, 2014
b120210
Warm compile cache after install
kevinsawicki Oct 8, 2014
014d565
Call callback at end of links commmand
kevinsawicki Oct 15, 2014
fb8420d
Add rebuild module cache command
kevinsawicki Oct 15, 2014
47f29a1
Call rebuild module cache command from install command
kevinsawicki Oct 15, 2014
e013328
:memo: Mention ignoring cache errors
kevinsawicki Oct 15, 2014
a3a7380
Call rebuild on command instance
kevinsawicki Oct 15, 2014
fb1f9fa
Call callback after creating
kevinsawicki Oct 15, 2014
e0f09b4
Use forEach to iterate over package folders
kevinsawicki Oct 15, 2014
d380f82
Don't catch errors thrown by calling callback
kevinsawicki Oct 15, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Gruntfile.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ module.exports = (grunt) ->
grunt.file.delete('bin/node_darwin_x64') if grunt.file.exists('bin/node_darwin_x64')

grunt.registerTask('lint', ['coffeelint'])
grunt.registerTask('default', ['coffee', 'coffeelint:src'])
grunt.registerTask('test', ['clean', 'default', 'coffeelint:test', 'shell:test'])
grunt.registerTask('default', ['coffee', 'lint'])
grunt.registerTask('test', ['clean', 'default', 'shell:test'])
grunt.registerTask('prepublish', ['clean', 'coffee', 'lint'])
26 changes: 26 additions & 0 deletions spec/fixtures/install-multi-version.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"releases": {
"latest": "0.4.0"
},
"name": "multi-module",
"versions": {
"0.4.0": {
"engines": {
"atom": ">=2.0"
}
},
"0.3.0": {
"engines": {
"atom": ">=1.0"
},
"dist": {
"tarball": "http://localhost:3000/tarball/test-module-1.0.0.tgz"
}
},
"0.2.0": {
"engines": {
"atom": ">=1.0"
}
}
}
}
50 changes: 49 additions & 1 deletion spec/install-spec.coffee
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
path = require 'path'
CSON = require 'season'
fs = require 'fs-plus'
temp = require 'temp'
express = require 'express'
Expand All @@ -7,7 +8,7 @@ wrench = require 'wrench'
apm = require '../lib/apm-cli'

describe 'apm install', ->
atomHome = null
[atomHome, resourcePath] = []

beforeEach ->
spyOnToken()
Expand All @@ -16,6 +17,9 @@ describe 'apm install', ->
atomHome = temp.mkdirSync('apm-home-dir-')
process.env.ATOM_HOME = atomHome

resourcePath = temp.mkdirSync('atom-resource-path-')
process.env.ATOM_RESOURCE_PATH = resourcePath

describe "when installing an atom package", ->
server = null

Expand All @@ -41,6 +45,8 @@ describe 'apm install', ->
response.sendfile path.join(__dirname, 'fixtures', 'test-module-with-symlink-5.0.0.tgz')
app.get '/tarball/test-module-with-bin-2.0.0.tgz', (request, response) ->
response.sendfile path.join(__dirname, 'fixtures', 'test-module-with-bin-2.0.0.tgz')
app.get '/packages/multi-module', (request, response) ->
response.sendfile path.join(__dirname, 'fixtures', 'install-multi-version.json')

server = http.createServer(app)
server.listen(3000)
Expand Down Expand Up @@ -86,6 +92,35 @@ describe 'apm install', ->
expect(fs.existsSync(path.join(testModuleDirectory, 'package.json'))).toBeTruthy()
expect(callback.mostRecentCall.args[0]).toBeNull()

describe 'when multiple releases are available', ->
it 'installs the latest compatible version', ->
CSON.writeFileSync(path.join(resourcePath, 'package.json'), version: '1.5.0')
packageDirectory = path.join(atomHome, 'packages', 'test-module')

callback = jasmine.createSpy('callback')
apm.run(['install', 'multi-module'], callback)

waitsFor 'waiting for install to complete', 600000, ->
callback.callCount is 1

runs ->
expect(JSON.parse(fs.readFileSync(path.join(packageDirectory, 'package.json'))).version).toBe "1.0.0"
expect(callback.mostRecentCall.args[0]).toBeNull()

it 'logs an error when no compatible versions are available', ->
CSON.writeFileSync(path.join(resourcePath, 'package.json'), version: '0.9.0')
packageDirectory = path.join(atomHome, 'packages', 'test-module')

callback = jasmine.createSpy('callback')
apm.run(['install', 'multi-module'], callback)

waitsFor 'waiting for install to complete', 600000, ->
callback.callCount is 1

runs ->
expect(fs.existsSync(packageDirectory)).toBeFalsy()
expect(callback.mostRecentCall.args[0]).not.toBeNull()

describe 'when multiple package names are specified', ->
it 'installs all packages', ->
testModuleDirectory = path.join(atomHome, 'packages', 'test-module')
Expand Down Expand Up @@ -200,3 +235,16 @@ describe 'apm install', ->

runs ->
expect(callback.mostRecentCall.args[0]).not.toBeNull()

describe 'when the package is bundled with Atom', ->
it 'logs a message to standard error', ->
CSON.writeFileSync(path.join(resourcePath, 'package.json'), packageDependencies: 'test-module': '1.0')

callback = jasmine.createSpy('callback')
apm.run(['install', 'test-module'], callback)

waitsFor 'waiting for install to complete', 600000, ->
callback.callCount is 1

runs ->
expect(console.error.mostRecentCall.args[0].length).toBeGreaterThan 0
1 change: 1 addition & 0 deletions src/apm-cli.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ commandClasses = [
require './login'
require './publish'
require './rebuild'
require './rebuild-module-cache'
require './search'
require './star'
require './stars'
Expand Down
3 changes: 2 additions & 1 deletion src/dedupe.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ class Dedupe extends Command
fs.makeTreeSync(@atomNodeDirectory)

run: (options) ->
{callback} = options
{callback, cwd} = options
options = @parseOptions(options.commandArgs)
options.cwd = cwd

@createAtomDirectories()

Expand Down
92 changes: 87 additions & 5 deletions src/install.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ async = require 'async'
_ = require 'underscore-plus'
optimist = require 'optimist'
CSON = require 'season'
semver = require 'semver'
temp = require 'temp'

config = require './config'
Command = require './command'
fs = require './fs'
RebuildModuleCache = require './rebuild-module-cache'
request = require './request'

module.exports =
Expand Down Expand Up @@ -125,6 +127,10 @@ class Install extends Command
destination = path.join(@atomPackagesDirectory, child)
do (source, destination) ->
commands.push (callback) -> fs.cp(source, destination, callback)

commands.push (callback) => @buildModuleCache(pack.name, callback)
commands.push (callback) => @warmCompileCache(pack.name, callback)

async.waterfall commands, (error) =>
if error?
@logFailure()
Expand Down Expand Up @@ -184,7 +190,7 @@ class Install extends Command
message = body.message ? body.error ? body
callback("Request for package information failed: #{message}")
else
if latestVersion = body.releases.latest
if body.releases.latest
callback(null, body)
else
callback("No releases available for #{packageName}")
Expand Down Expand Up @@ -273,14 +279,18 @@ class Install extends Command
@logFailure()
callback(error)
else
commands = []
packageVersion ?= pack.releases.latest
packageVersion ?= @getLatestCompatibleVersion(pack)
unless packageVersion
@logFailure()
callback("No available version compatible with the installed Atom version: #{@installedAtomVersion}")

{tarball} = pack.versions[packageVersion]?.dist ? {}
unless tarball
@logFailure()
callback("Package version: #{packageVersion} not found")
return

commands = []
commands.push (callback) =>
if packagePath = @getPackageCachePath(packageName, packageVersion)
callback(null, packagePath)
Expand Down Expand Up @@ -376,6 +386,68 @@ class Install extends Command
packages = fs.readFileSync(filePath, 'utf8')
@sanitizePackageNames(packages.split(/\s/))

getResourcePath: (callback) ->
if @resourcePath
process.nextTick => callback(@resourcePath)
else
config.getResourcePath (@resourcePath) => callback(@resourcePath)

buildModuleCache: (packageName, callback) ->
packageDirectory = path.join(@atomPackagesDirectory, packageName)
rebuildCacheCommand = new RebuildModuleCache()
rebuildCacheCommand.rebuild packageDirectory, ->
# Ignore cache errors and just finish the install
callback()

warmCompileCache: (packageName, callback) ->
packageDirectory = path.join(@atomPackagesDirectory, packageName)

@getResourcePath (resourcePath) ->
try
CoffeeCache = require(path.join(resourcePath, 'src', 'coffee-cache'))

onDirectory = (directoryPath) ->
path.basename(directoryPath) isnt 'node_modules'

onFile = (filePath) ->
CoffeeCache.addPathToCache(filePath)

fs.traverseTreeSync(packageDirectory, onFile, onDirectory)
callback(null)

isBundledPackage: (packageName, callback) ->
@getResourcePath (resourcePath) ->
try
atomMetadata = JSON.parse(fs.readFileSync(path.join(resourcePath, 'package.json')))
catch error
return callback(false)

callback(atomMetadata?.packageDependencies?.hasOwnProperty(packageName))

getLatestCompatibleVersion: (pack) ->
return pack.releases.latest unless @installedAtomVersion

latestVersion = null
for version, metadata of pack.versions ? {}
continue unless semver.valid(version)
continue unless metadata

engine = metadata.engines?.atom ? '*'
continue unless semver.validRange(engine)
continue unless semver.satisfies(@installedAtomVersion, engine)

latestVersion ?= version
latestVersion = version if semver.gt(version, latestVersion)

latestVersion

loadInstalledAtomVersion: (callback) ->
@getResourcePath (resourcePath) =>
try
{version} = require(path.join(resourcePath, 'package.json')) ? {}
@installedAtomVersion = version if semver.valid(version)
callback()

run: (options) ->
{callback} = options
options = @parseOptions(options.commandArgs)
Expand All @@ -393,9 +465,16 @@ class Install extends Command
if atIndex > 0
version = name.substring(atIndex + 1)
name = name.substring(0, atIndex)
@installPackage({name, version}, options, callback)

commands = []
@isBundledPackage name, (isBundledPackage) =>
if isBundledPackage
console.error """
The #{name} package is bundled with Atom and should not be explicitly installed.
You can run `apm uninstall #{name}` to uninstall it and then the version bundled
with Atom will be used.
""".yellow
@installPackage({name, version}, options, callback)

if packagesFilePath
try
packageNames = @packageNamesFromPath(packagesFilePath)
Expand All @@ -404,6 +483,9 @@ class Install extends Command
else
packageNames = @packageNamesFromArgv(options.argv)
packageNames.push('.') if packageNames.length is 0

commands = []
commands.push (callback) => @loadInstalledAtomVersion(callback)
packageNames.forEach (packageName) ->
commands.push (callback) -> installPackage(packageName, callback)
async.waterfall(commands, callback)
5 changes: 4 additions & 1 deletion src/links.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class Links extends Command
realpath = '???'.red
"#{path.basename(link).yellow} -> #{realpath}"

run: ->
run: (options) ->
{callback} = options

@logLinks(@devPackagesPath)
@logLinks(@packagesPath)
callback()
48 changes: 48 additions & 0 deletions src/rebuild-module-cache.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
path = require 'path'
async = require 'async'
Command = require './command'
config = require './config'
fs = require './fs'

module.exports =
class RebuildModuleCache extends Command
@commandNames: ['rebuild-module-cache']

constructor: ->
@atomPackagesDirectory = path.join(config.getAtomDirectory(), 'packages')

getResourcePath: (callback) ->
if @resourcePath
process.nextTick => callback(@resourcePath)
else
config.getResourcePath (@resourcePath) => callback(@resourcePath)

rebuild: (packageDirectory, callback) ->
@getResourcePath (resourcePath) =>
try
@moduleCache ?= require(path.join(resourcePath, 'src', 'module-cache'))
@moduleCache.create(packageDirectory)
catch error
return callback(error)

callback()

run: (options) ->
{callback} = options

commands = []
fs.list(@atomPackagesDirectory).forEach (packageName) =>
packageDirectory = path.join(@atomPackagesDirectory, packageName)
return if fs.isSymbolicLinkSync(packageDirectory)
return unless fs.isDirectorySync(packageDirectory)

commands.push (callback) =>
process.stdout.write "Rebuilding #{packageName} module cache "
@rebuild packageDirectory, (error) =>
if error?
@logFailure()
else
@logSuccess()
callback(error)

async.waterfall(commands, callback)