diff --git a/changelog.md b/changelog.md index 2cf956d..730c68e 100644 --- a/changelog.md +++ b/changelog.md @@ -6,7 +6,7 @@ ### Added -- TBA +- Added support for automated dependency management (`autoinstall`) --- diff --git a/cli.js b/cli.js index bc513a8..31f4a50 100755 --- a/cli.js +++ b/cli.js @@ -16,12 +16,14 @@ let isShared = opt => opt === 'shared' || opt === '--shared' || opt === '-s' let isUpdate = opt => opt === 'update' || opt === '--update' || opt === '-u' || opt === 'upgrade' || opt === '--upgrade' // jic let isVerbose = opt => opt === 'verbose' || opt === '--verbose' || opt === '-v' +let isAutoinstall = opt => opt === 'autoinstall' || opt === '--autoinstall' // eslint-disable-next-line async function cmd (opts = []) { let args = { - verbose: opts.some(isVerbose) + verbose: opts.some(isVerbose), + autoinstall: opts.some(isAutoinstall), } if (opts.some(isShared)) { diff --git a/src/_cleanup.js b/src/_cleanup.js new file mode 100644 index 0000000..8357b3f --- /dev/null +++ b/src/_cleanup.js @@ -0,0 +1,23 @@ +let { mkdirSync, renameSync } = require('fs') +let { join } = require('path') + +// Best effort local artifact cleanup +module.exports = function cleanup (installed) { + if (installed) { + try { + installed.forEach(i => { + let { dir, file, remove } = i + if (file === 'package.json') { + let dest = join(dir, 'node_modules', '_arc-autoinstall') + mkdirSync(dest, { recursive: true }) + remove.forEach(f => { + let before = join(dir, f) + let after = join(dest, f) + renameSync(before, after) + }) + } + }) + } + catch (err) { null } // Swallow errors, we may have to bubble something else + } +} diff --git a/src/actions/autoinstall/index.js b/src/actions/autoinstall/index.js index 0e9b969..b100668 100644 --- a/src/actions/autoinstall/index.js +++ b/src/actions/autoinstall/index.js @@ -2,16 +2,15 @@ let { existsSync, readFileSync, writeFileSync } = require('fs') let { join } = require('path') let { sync: glob } = require('glob') let rm = require('rimraf').sync -let { ignoreDeps, stripCwd } = require('../../lib') +let { ignoreDeps } = require('../../lib') let getRequires = require('./get-requires') module.exports = function autoinstaller (params) { let { dirs, inventory, update, verbose } = params if (!dirs.length) return [] - let writes = [] // Manifests to be written if there are no parsing failures + let installing = [] // Generated manifests to be hydrated later (if there are no parsing failures) let failures = [] // Userland files that could not be parsed - let installing = [] // Generated manifests to be hydrated later // Get package.json contents and map out root dependencies let packageJson = join(process.cwd(), 'package.json') @@ -64,14 +63,17 @@ module.exports = function autoinstaller (params) { let dependencies = {} dirDeps.forEach(dep => dependencies[dep] = packageJsonDeps[dep] || 'latest') let lambdaPackage = { - _arc: 'hydrate-autoinstall', - description: `This file, .arc-autoinstall, and package-lock.json should have been deleted by Architect after deployment; all can be safely be removed`, + _arc: 'autoinstall', + _module: 'hydrate', + _date: new Date().toISOString(), + _parsed: files, + description: `This file was generated by Architect, and placed in node_modules to aid in debugging; if you found file in your function directory, you can safely remove it (and package-lock.json)`, dependencies, } - writes.push({ + installing.push({ dir, file: 'package.json', - del: 'package.json\npackage-lock.json', // Identify files for later deletion + remove: [ 'package.json', 'package-lock.json' ], // Identify files for later removal data: JSON.stringify(lambdaPackage, null, 2) }) } @@ -92,12 +94,9 @@ module.exports = function autoinstaller (params) { } // Write everything at the end in case there were any parsing errors - writes.forEach(({ dir, file, data, del }) => { - let marker = join(dir, '.arc-autoinstall') + installing.forEach(({ dir, file, data }) => { let manifest = join(dir, file) - writeFileSync(marker, del) writeFileSync(manifest, data) - installing.push(stripCwd(manifest)) }) if (verbose) { diff --git a/src/index.js b/src/index.js index bbcef29..96bd3f0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,13 @@ let { sync: glob } = require('glob') let series = require('run-series') -let { dirname } = require('path') +let { dirname, join } = require('path') let stripAnsi = require('strip-ansi') let { pathToUnix, updater } = require('@architect/utils') let inventory = require('@architect/inventory') let { isDep, ignoreDeps, stripCwd } = require('./lib') let shared = require('./shared') let actions = require('./actions') +let cleanup = require('./_cleanup') /** * Installs deps into or updates current deps in: @@ -41,7 +42,7 @@ function hydrator (inventory, installing, params, callback) { copyShared = true, hydrateShared = true } = params - + let autoinstalled // Assigned later let action = installing ? 'Hydrat' : 'Updat' // Used in logging below // From here on out normalize all file comparisons to Unix paths @@ -117,13 +118,17 @@ function hydrator (inventory, installing, params, callback) { let ops = [] // Run the autoinstaller first in case we need to add any new manifests to the ops - if (autoinstall) { + if (autoinstall && installing) { // Ignore directories already known to have a manifest let dirs = inv.lambdaSrcDirs.filter(d => !files.some(file => dirname(file) === pathToUnix(stripCwd(d)))) // Allow scoping to a single directory if (basepath) dirs = dirs.filter(d => pathToUnix(stripCwd(d)) === pathToUnix(stripCwd(basepath))) - let installing = actions.autoinstall({ dirs, update, inventory, ...params }) - files = files.concat(installing) + let result = actions.autoinstall({ dirs, update, inventory, ...params }) + if (result.length) { + autoinstalled = result + let install = autoinstalled.map(({ dir, file }) => stripCwd(join(dir, file))) + files = files.concat(install) + } } // Install + update @@ -143,6 +148,9 @@ function hydrator (inventory, installing, params, callback) { } series(ops, function done (err, result) { + // Tidy up before exiting + cleanup(autoinstalled) + result = [].concat.apply([], result) // Flatten the nested shared array if (init) result.unshift(init) // Bump init logging to the top if (err) callback(err, result) diff --git a/src/shared/index.js b/src/shared/index.js index 6c2ecfc..f3d98ae 100644 --- a/src/shared/index.js +++ b/src/shared/index.js @@ -37,7 +37,7 @@ module.exports = function shared (params = {}, callback) { if (shared || views) { start = update.status('Hydrating app with shared files') } - paths = getPaths(inventory, 'shared') + paths = getPaths(inventory) callback() }, function (callback) { diff --git a/test/integration/default/install-tests.js b/test/integration/default/install-tests.js index 9d97f8d..ddda997 100644 --- a/test/integration/default/install-tests.js +++ b/test/integration/default/install-tests.js @@ -73,8 +73,8 @@ test(`[Default (file copying)] install() hydrates all Functions', shared and vie t.ok(existsSync(p), `node views dependency exists at ${p}`) }) // Autoinstall-specific tests - let marker = join(arcAutoinstall[0], '.arc-autoinstall') - t.ok(existsSync(marker), 'Found autoinstall marker file') + let package = join(arcAutoinstall[0], 'node_modules', '_arc-autoinstall', 'package.json') + t.ok(existsSync(package), 'Found autoinstall package.json') // Yarn-specific tests let yarnFunction = join(mockTmp, 'src', 'http', 'put-on_your_boots') let yarnIntFile = join(yarnFunction, 'node_modules', '.yarn-integrity') diff --git a/test/integration/symlink/install-tests.js b/test/integration/symlink/install-tests.js index 620dab5..839b0a7 100644 --- a/test/integration/symlink/install-tests.js +++ b/test/integration/symlink/install-tests.js @@ -77,8 +77,8 @@ test(`[Symlinking] install() with symlink hydrates all Functions', shared and vi t.ok(existsSync(p), `node views dependency exists at ${p}`) }) // Autoinstall-specific tests - let marker = join(arcAutoinstall[0], '.arc-autoinstall') - t.ok(existsSync(marker), 'Found autoinstall marker file') + let package = join(arcAutoinstall[0], 'node_modules', '_arc-autoinstall', 'package.json') + t.ok(existsSync(package), 'Found autoinstall package.json') // Yarn-specific tests let yarnFunction = join(mockTmp, 'src', 'http', 'put-on_your_boots') let yarnIntFile = join(yarnFunction, 'node_modules', '.yarn-integrity')