From 2ed4ff6fd5616d1923c768b3e51caa46efb4ff65 Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Wed, 17 Apr 2024 06:38:58 -0700 Subject: [PATCH] chore: WIP --- lib/aws/request.js | 2 +- lib/classes/plugin-manager.js | 88 ++++++++++++------- lib/cli/commands-schema.js | 47 ---------- lib/plugins/aws/config-credentials.js | 76 ---------------- lib/plugins/aws/deploy-function.js | 34 ++++--- lib/plugins/aws/deploy-list.js | 2 +- lib/plugins/aws/deploy/index.js | 31 ++++--- lib/plugins/aws/deploy/lib/create-stack.js | 2 +- .../deploy/lib/ensure-valid-bucket-exists.js | 2 +- lib/plugins/aws/info/index.js | 1 + lib/plugins/aws/invoke-local/index.js | 2 +- .../compile/events/api-gateway/index.js | 9 +- lib/plugins/aws/package/index.js | 9 +- lib/plugins/aws/remove/index.js | 11 +-- lib/plugins/aws/rollback.js | 2 +- lib/plugins/config.js | 42 --------- lib/plugins/index.js | 3 - lib/plugins/install.js | 44 ---------- lib/plugins/package/lib/zip-service.js | 24 ++--- lib/plugins/plugin/lib/utils.js | 2 +- lib/serverless-error.js | 1 - 21 files changed, 129 insertions(+), 305 deletions(-) delete mode 100644 lib/plugins/aws/config-credentials.js delete mode 100644 lib/plugins/config.js delete mode 100644 lib/plugins/install.js diff --git a/lib/aws/request.js b/lib/aws/request.js index 0bacbd4cd..36ae73811 100644 --- a/lib/aws/request.js +++ b/lib/aws/request.js @@ -183,7 +183,7 @@ async function awsRequest(service, method, ...args) { awsLog.debug(`request result: #${requestId} ${service.name}.${method}`, result); return result; } catch (err) { - awsLog.debug(`request error: #${requestId} - ${service.name}.${method}`, error); + awsLog.debug(`request error: #${requestId} - ${service.name}.${method}`, err); let message = err.message != null ? err.message : String(err.code); if (message.startsWith('Missing credentials in config')) { // Credentials error diff --git a/lib/classes/plugin-manager.js b/lib/classes/plugin-manager.js index 9a86434bf..47aca1a38 100644 --- a/lib/classes/plugin-manager.js +++ b/lib/classes/plugin-manager.js @@ -1,13 +1,15 @@ import path from 'path'; +import fs from 'fs/promises'; import _ from 'lodash'; import utils from '@serverlessinc/sf-core/src/utils.js'; import ServerlessError from '../serverless-error.js'; import renderCommandHelp from '../cli/render-help/command.js'; import tokenizeException from '../utils/tokenize-exception.js'; +import { fileURLToPath, pathToFileURL } from 'url'; import getRequire from '../utils/get-require.js'; import importModule from '../utils/require-with-import-fallback.js'; -const { log, getPluginWriters } = utils; +const { log, getPluginWriters, readFile } = utils; let hooksIdCounter = 0; let nestTracker = 0; @@ -139,43 +141,66 @@ class PluginManager { return this.asyncPluginInit(); } - async requireServicePlugin(serviceDir, pluginPath, legacyLocalPluginsPath) { - if (legacyLocalPluginsPath && !pluginPath.startsWith('./')) { - // TODO (BREAKING): Consider removing support for localPluginsPath with next major - const absolutePluginPath = path.resolve(legacyLocalPluginsPath, pluginPath); - const absolutePluginPathURL = pathToFileURL(absolutePluginPath).href; + /** + * Return a promise to require a Service plugin, whether it's local in a relative path, + * local in the service directory, or a node_module + * @param {*} serviceDir + * @param {*} pluginListedName + * @param {*} legacyLocalPluginsPath + * @returns + */ + async requireServicePlugin(serviceDir, pluginListedName, legacyLocalPluginsPath) { - const isLocatedAtLegacyPluginsPath = await (async () => { - try { - await import.meta.resolve(absolutePluginPathURL); - return true; - } catch { - return false; - } - })(); - if (isLocatedAtLegacyPluginsPath) return import(absolutePluginPathURL); - } - - const serviceDirRequire = (dir) => (module) => import.meta.resolve(pathToFileURL(path.resolve(dir, module)).href); - const localPluginPath = await (async () => { + /** + * Check the Service directory for the plugin. + * This will check in the node_modules of the service directory + * and in the service directory itself. + */ + const serviceDirRequire = async (dir, module) => { + // Attempt to construct the full path for local modules or node_modules + const fullPath = path.resolve(dir, module); try { - return await serviceDirRequire(serviceDir)(pluginPath); - } catch { - return null; + // Convert the path to a URL and dynamically import the module + const moduleUrl = pathToFileURL(fullPath).href; + await import(moduleUrl); + return moduleUrl; // Return URL if module can be imported successfully + } catch (error) { + // If direct path import fails, try resolving under node_modules + const packageJsonPath = path.resolve(dir, 'node_modules', module, 'package.json'); + // IMport JSON in ESM + const packageJson = await readFile(packageJsonPath); + if (!packageJson) { + throw Object.assign(new Error('Plugin not found'), { code: 'PLUGIN_NOT_FOUND' }); + } + let { main } = JSON.parse(packageJson); + if (!main) { main = 'plugin.js'; } + const pluginPath = path.resolve(dir, 'node_modules', module, main); + const pluginUrl = pathToFileURL(pluginPath).href; + try { + await import(pluginUrl); + return pluginUrl; // Return URL if module can be imported successfully + } catch (error) { + throw Object.assign(new Error('Plugin not found'), { code: 'PLUGIN_NOT_FOUND' }); + } } - })(); + }; + + const localPluginPath = await serviceDirRequire(serviceDir, pluginListedName); + if (localPluginPath) { - if (pluginPath.startsWith('./')) { - this.localPluginsPaths.push({ resolvedPath: fileURLToPath(localPluginPath), inputPath: pluginPath }); + if (pluginListedName.startsWith('./')) { + this.localPluginsPaths.push({ resolvedPath: fileURLToPath(localPluginPath), inputPath: pluginListedName }); } return import(localPluginPath); } - // Search in "node_modules" in which framework is placed + /** + * Search in the Framework's node_modules + */ const externalPluginPath = await (async () => { try { - return await import.meta.resolve(pluginPath); - } catch { + return await import(pluginListedName); + } catch (error) { throw Object.assign(new Error('Plugin not found'), { code: 'PLUGIN_NOT_FOUND' }); } })(); @@ -193,6 +218,7 @@ class PluginManager { let Plugin; try { Plugin = await this.requireServicePlugin(serviceDir, name, pluginsObject.localPath); + Plugin = Plugin.default || Plugin; } catch (error) { if (error.code !== 'PLUGIN_NOT_FOUND') throw error; @@ -561,8 +587,7 @@ class PluginManager { log .get('sls:lifecycle:command:invoke') .debug( - `Invoke ${commandsArray.join(':')}${ - !hooksLength ? ' (noop due to no registered hooks)' : '' + `Invoke ${commandsArray.join(':')}${!hooksLength ? ' (noop due to no registered hooks)' : '' }` ); @@ -628,8 +653,7 @@ class PluginManager { } catch (errorHookException) { const errorHookExceptionMeta = tokenizeException(errorHookException); log.warning( - `The "error" hook crashed with:\n${ - errorHookExceptionMeta.stack || errorHookExceptionMeta.message + `The "error" hook crashed with:\n${errorHookExceptionMeta.stack || errorHookExceptionMeta.message }` ); } finally { diff --git a/lib/cli/commands-schema.js b/lib/cli/commands-schema.js index c09956179..39f343142 100644 --- a/lib/cli/commands-schema.js +++ b/lib/cli/commands-schema.js @@ -14,53 +14,6 @@ commands.commonOptions = globalOptions; // Export the commands Map and any other properties collectively export default commands; -commands.set('config credentials', { - usage: 'Configures a new provider profile for the Serverless Framework', - hasAwsExtension: true, - options: { - provider: { - type: 'string', - usage: 'Name of the provider. Supported providers: aws', - required: true, - shortcut: 'p', - }, - key: { - type: 'string', - usage: 'Access key for the provider', - shortcut: 'k', - required: true, - }, - secret: { - type: 'string', - usage: 'Secret key for the provider', - shortcut: 's', - required: true, - }, - profile: { - type: 'string', - usage: 'Name of the profile you wish to create. Defaults to "default"', - shortcut: 'n', - }, - overwrite: { - usage: 'Overwrite the existing profile configuration in the credentials file', - shortcut: 'o', - type: 'boolean', - }, - }, - lifecycleEvents: ['config'], -}); - -commands.set('config', { - usage: 'Configure Serverless', - options: { - autoupdate: { - usage: 'Turn on auto update mechanism (turn off via "--no-autoupdate")', - type: 'boolean', - }, - }, - lifecycleEvents: ['config'], -}); - commands.set('plugin install', { usage: 'Install and add a plugin to your service', options: { diff --git a/lib/plugins/aws/config-credentials.js b/lib/plugins/aws/config-credentials.js deleted file mode 100644 index 8401e20ce..000000000 --- a/lib/plugins/aws/config-credentials.js +++ /dev/null @@ -1,76 +0,0 @@ -import os from 'os'; -import credentials from './utils/credentials.js'; -import ServerlessError from '../../serverless-error.js'; -import cliCommandsSchema from '../../cli/commands-schema.js'; -import utils from '@serverlessinc/sf-core/src/utils.js'; - -const { log } = utils; - -class AwsConfigCredentials { - constructor(serverless, options) { - this.serverless = serverless; - this.options = options; - // Note: we're not setting the provider here as this plugin should also be - // run when the CWD is not an AWS service - - // this will be merged with the core config commands - this.commands = { - config: { - commands: { - credentials: { - ...cliCommandsSchema.get('config credentials'), - }, - }, - }, - }; - - if (!os.homedir()) { - throw new ServerlessError( - "Can't find home directory on your local file system.", - 'MISSING_HOME_DIRECTORY' - ); - } - - this.hooks = { - 'config:credentials:config': async () => this.configureCredentials(), - }; - } - - async configureCredentials() { - // sanitize - this.options.provider = this.options.provider.toLowerCase(); - this.options.profile = this.options.profile ? this.options.profile : 'default'; - - // resolve if provider option is not 'aws' - if (this.options.provider !== 'aws') return null; - - // validate - if (!this.options.key || !this.options.secret) { - throw new ServerlessError( - 'Please include --key and --secret options for AWS.', - 'MISSING_KEY_AND_SECRET_CLI_OPTIONS' - ); - } - - const profiles = await credentials.resolveFileProfiles(); - if (profiles.has(this.options.profile)) { - // Only update the profile if the overwrite flag was set - if (!this.options.overwrite) { - throw new ServerlessError( - `Profile "${this.options.profile}" is already configured in ~/.aws/credentials. Use the overwrite flag ("-o" or "--overwrite") to force the update.`, - 'CREDENTIALS_PROFILE_ALREADY_CONFIGURED' - ); - } - } - profiles.set(this.options.profile, { - accessKeyId: this.options.key, - secretAccessKey: this.options.secret, - }); - - const result = await credentials.saveFileProfiles(profiles); - log.notice.success(`Profile "${this.options.profile}" has been configured`); - return result; - } -} - -export default AwsConfigCredentials; diff --git a/lib/plugins/aws/deploy-function.js b/lib/plugins/aws/deploy-function.js index 79d0763ce..051d710c3 100644 --- a/lib/plugins/aws/deploy-function.js +++ b/lib/plugins/aws/deploy-function.js @@ -28,24 +28,26 @@ class AwsDeployFunction { 'initialize': () => { const commandName = this.serverless.processedInput.commands.join(' '); if (commandName !== 'deploy function') return; - this.logger.notice(); this.logger.notice( - `Deploying function ${this.options.function} to stage ${this.serverless + `Deploying function "${this.options.function}" to stage "${this.serverless .getProvider('aws') - .getStage()} ${this.loggerStyle.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}` + .getStage()}" ${this.loggerStyle.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}` ); this.logger.notice(); }, 'before:deploy:function:initialize': () => this.progress.notice('Validating', { isMainEvent: true }), 'deploy:function:initialize': async () => { + this.logger.debug('validating...'); await this.validate(); + this.logger.debug('checking if function exists...') await this.checkIfFunctionExists(); + this.logger.debug('checking if function changed...') this.checkIfFunctionChangesBetweenImageAndHandler(); }, 'before:deploy:function:packageFunction': () => - this.progress.notice('Retrieving function info', { isMainEvent: true }), + this.progress.notice('Retrieving function info...', { isMainEvent: true }), 'deploy:function:packageFunction': async () => this.serverless.pluginManager.spawn('package:function'), @@ -53,6 +55,7 @@ class AwsDeployFunction { this.progress.notice('Packaging', { isMainEvent: true }), 'deploy:function:deploy': async () => { if (!this.options['update-config']) { + this.logger.debug('deploying function code...') await this.deployFunction(); } await this.updateFunctionConfiguration(); @@ -65,6 +68,8 @@ class AwsDeployFunction { } async checkIfFunctionExists() { + this.progress.notice('Checking for changes...'); + // check if the function exists in the service this.options.functionObj = this.serverless.service.getFunction(this.options.function); @@ -178,6 +183,8 @@ class AwsDeployFunction { } async callUpdateFunctionConfiguration(params) { + this.logger.debug('deploying function configuration changes...') + const startTime = Date.now(); const callWithRetry = async () => { @@ -440,13 +447,13 @@ class AwsDeployFunction { } if (!Object.keys(_.omit(params, 'FunctionName')).length) { - if (this.options['update-config']) this.logger.notice(); + this.logger.notice(); const noticeMessage = [ - 'Function configuration did not change, and the update was skipped.', + 'Function configuration did not change, so the update was skipped.', ' If you made changes to the service configuration and expected them to be deployed,', - ' it most likely means that they can only be applied with a full service deployment.', + ' this most likely means that they can only be applied with a full service deployment: "serverless deploy".', ].join(''); - this.logger.notice.skip( + this.logger.aside( `${noticeMessage} ${this.loggerStyle.aside( `(${Math.floor( (Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000 @@ -461,7 +468,7 @@ class AwsDeployFunction { await this.callUpdateFunctionConfiguration(params); this.shouldEnsureFunctionState = true; if (this.options['update-config']) this.logger.notice(); - this.logger.notice.success( + this.logger.success( `Function configuration updated ${this.loggerStyle.aside( `(${Math.floor((Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000)}s)` )}\n` @@ -482,7 +489,7 @@ class AwsDeployFunction { this.serverless.service.provider.remoteFunctionData.Configuration.CodeSha256; if (remoteImageSha === functionImageSha && !this.options.force) { this.logger.notice(); - this.logger.notice.skip( + this.logger.notice( `Image did not change. Function deployment skipped. ${this.loggerStyle.aside( `(${Math.floor( (Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000 @@ -509,7 +516,7 @@ class AwsDeployFunction { if (remoteHash === localHash && !this.options.force) { this.logger.notice(); - this.logger.notice.skip( + this.logger.aside( `Code did not change. Function deployment skipped. ${this.loggerStyle.aside( `(${Math.floor( (Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000 @@ -522,16 +529,15 @@ class AwsDeployFunction { params.ZipFile = data; const stats = fs.statSync(artifactFilePath); - this.progress.notice(`Uploading ${this.loggerStyle.aside(`(${filesize(stats.size)})`)}`, { + this.progress.notice(`Uploading (${filesize(stats.size)})...`, { isMainEvent: true, }); } - this.progress.notice('Deploying', { isMainEvent: true }); await this.provider.request('Lambda', 'updateFunctionCode', params); this.shouldEnsureFunctionState = true; this.logger.notice(); - this.logger.notice.success( + this.logger.success( `Function code deployed ${this.loggerStyle.aside( `(${Math.floor((Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000)}s)` )}` diff --git a/lib/plugins/aws/deploy-list.js b/lib/plugins/aws/deploy-list.js index fd64afece..e7e8844d1 100644 --- a/lib/plugins/aws/deploy-list.js +++ b/lib/plugins/aws/deploy-list.js @@ -52,7 +52,7 @@ class AwsDeployList { if (deployments.length === 0) { log.notice(); - log.notice.skip( + log.aside( "No deployments found, if that's unexpected ensure that stage and region are correct" ); return; diff --git a/lib/plugins/aws/deploy/index.js b/lib/plugins/aws/deploy/index.js index 22caee8d3..cc046428c 100644 --- a/lib/plugins/aws/deploy/index.js +++ b/lib/plugins/aws/deploy/index.js @@ -23,8 +23,8 @@ import path from 'path'; import utils from '@serverlessinc/sf-core/src/utils.js'; import memoize from 'memoizee'; -const { log, style, progress, writeText } = utils; -const mainProgress = progress.get('main'); +const { log, style, progress } = utils; +const mainProgress = progress.get('sls:plugin:awsdeploy'); class AwsDeploy { constructor(serverless, options) { @@ -94,17 +94,15 @@ class AwsDeploy { this.provider.cachedCredentials, 'dashboardProviderAlias' ); - log.notice(); log.notice( - `Deploying ${this.serverless.service.service} to stage ${this.serverless + `Deploying "${this.serverless.service.service}" to stage "${this.serverless .getProvider('aws') - .getStage()} ${style.aside( + .getStage()}" ${style.aside( `(${this.serverless.getProvider('aws').getRegion()}${ dashboardProviderName ? `, "${dashboardProviderName}" provider` : '' })` )}` ); - log.info(); // Ensure gap between verbose logging // This is used to ensure that for `deploy` command, the `accountId` will be resolved and available // for `generatePayload` telemetry logic @@ -134,7 +132,7 @@ class AwsDeploy { // Deploy deploy inner lifecycle 'before:aws:deploy:deploy:createStack': () => - mainProgress.notice('Retrieving CloudFormation stack', { isMainEvent: true }), + mainProgress.notice('Retrieving CloudFormation stack'), 'aws:deploy:deploy:createStack': async () => this.createStack(), 'aws:deploy:deploy:checkForChanges': async () => { @@ -153,7 +151,7 @@ class AwsDeploy { 'before:aws:deploy:deploy:uploadArtifacts': () => { if (this.serverless.service.provider.shouldNotDeploy) return; - mainProgress.notice('Uploading', { isMainEvent: true }); + mainProgress.notice('Uploading'); }, 'aws:deploy:deploy:uploadArtifacts': async () => { if (this.serverless.service.provider.shouldNotDeploy) return; @@ -202,8 +200,7 @@ class AwsDeploy { 'finalize': async () => { if (this.serverless.processedInput.commands.join(' ') !== 'deploy') return; if (this.serverless.service.provider.shouldNotDeploy) { - log.notice(); - log.notice.skip( + log.aside( `No changes to deploy. Deployment skipped. ${style.aside( `(${Math.floor( (Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000 @@ -214,8 +211,7 @@ class AwsDeploy { } if (this.serverless.service.provider.deploymentWithEmptyChangeSet) { - log.notice(); - log.notice.skip( + log.aside( `Change set did not include any changes to be deployed. ${style.aside( `(${Math.floor( (Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000 @@ -225,8 +221,7 @@ class AwsDeploy { return; } - log.notice(); - log.notice.success( + log.success( `Service deployed to stack ${this.serverless .getProvider('aws') .naming.getStackName()} ${style.aside( @@ -235,16 +230,20 @@ class AwsDeploy { )}s)` )}` ); - writeText(); + + mainProgress.remove(); + writeServiceOutputs(this.serverless.serviceOutputs); writeServiceOutputs(this.serverless.servicePluginOutputs); if (this.options['enforce-hash-update']) { - log.notice(); + log.blankLine(); log.notice( 'Your service has been deployed with new hashing algorithm. Please remove "provider.lambdaHashingVersion" from your service configuration and re-deploy without "--enforce-hash-update" flag to restore function descriptions.' ); } + + log.blankLine(); }, }; } diff --git a/lib/plugins/aws/deploy/lib/create-stack.js b/lib/plugins/aws/deploy/lib/create-stack.js index 8e42fab2b..ccd3ea2dc 100644 --- a/lib/plugins/aws/deploy/lib/create-stack.js +++ b/lib/plugins/aws/deploy/lib/create-stack.js @@ -9,7 +9,7 @@ const inactiveStateNames = new Set(['REVIEW_IN_PROGRESS']); export default { async create() { // Note: using three dots instead of ellipsis to support non uni-code consoles. - progress.get('main').notice('Creating CloudFormation stack', { isMainEvent: true }); + progress.get('sls:plugin:awsdeploy').notice('Creating CloudFormation stack', { isMainEvent: true }); const stackName = this.provider.naming.getStackName(); let monitorCfData; diff --git a/lib/plugins/aws/deploy/lib/ensure-valid-bucket-exists.js b/lib/plugins/aws/deploy/lib/ensure-valid-bucket-exists.js index 692b3efd7..6e6b7e193 100644 --- a/lib/plugins/aws/deploy/lib/ensure-valid-bucket-exists.js +++ b/lib/plugins/aws/deploy/lib/ensure-valid-bucket-exists.js @@ -3,7 +3,7 @@ import utils from '@serverlessinc/sf-core/src/utils.js'; import jsyaml from 'js-yaml'; const { log, progress } = utils; -const mainProgress = progress.get('main'); +const mainProgress = progress.get('sls:plugin:awsdeploy'); export default { async ensureValidBucketExists() { diff --git a/lib/plugins/aws/info/index.js b/lib/plugins/aws/info/index.js index 1a5a288d0..7a2893027 100644 --- a/lib/plugins/aws/info/index.js +++ b/lib/plugins/aws/info/index.js @@ -71,6 +71,7 @@ class AwsInfo { if (this.serverless.processedInput.commands.join(' ') !== 'info') return; writeServiceOutputs(this.serverless.serviceOutputs); writeServiceOutputs(this.serverless.servicePluginOutputs); + this.logger.blankLine(); }, }; } diff --git a/lib/plugins/aws/invoke-local/index.js b/lib/plugins/aws/invoke-local/index.js index 76c922f53..b5628a51d 100644 --- a/lib/plugins/aws/invoke-local/index.js +++ b/lib/plugins/aws/invoke-local/index.js @@ -835,7 +835,7 @@ class AwsInvokeLocal { if (error.code === 'ERR_REQUIRE_ESM') { // Already in ESM format, use the utility if necessary or retry with a different method return await importEsm(`${modulePath}.js`); - } else if (error.code === 'MODULE_NOT_FOUND') { + } else if (error.code === 'MODULE_NOT_FOUND' || error.code === 'ERR_MODULE_NOT_FOUND') { // Attempt to import with `.js` extension const pathToHandler = `${modulePath}.js`; try { diff --git a/lib/plugins/aws/package/compile/events/api-gateway/index.js b/lib/plugins/aws/package/compile/events/api-gateway/index.js index aedc0e49d..5ff54904e 100644 --- a/lib/plugins/aws/package/compile/events/api-gateway/index.js +++ b/lib/plugins/aws/package/compile/events/api-gateway/index.js @@ -260,11 +260,14 @@ class AwsCompileApigEvents { // TODO should be removed once AWS fixes the CloudFormation problems using a separate Stage 'after:deploy:deploy': async () => { - const { getServiceState } = await import('../../../../lib/get-service-state.js'); - const { updateStage } = await import('./lib/hack/update-stage.js'); - + // Dynamically import the module + const module = await import('../../../../lib/get-service-state.js'); + // Access the default export and then the getServiceState method + const getServiceState = module.default.getServiceState.bind(this); const state = getServiceState.call(this); this.state = state; + const moduleTwo = await import('./lib/hack/update-stage.js'); + const updateStage = moduleTwo.default.updateStage.bind(this); return updateStage.call(this); }, diff --git a/lib/plugins/aws/package/index.js b/lib/plugins/aws/package/index.js index 7adba7699..b4590efda 100644 --- a/lib/plugins/aws/package/index.js +++ b/lib/plugins/aws/package/index.js @@ -70,11 +70,11 @@ class AwsPackage { if (this.serverless.processedInput.commands.join(' ') === 'package') { log.notice(); log.notice( - `Packaging ${this.serverless.service.service} for stage ${this.serverless + `Packaging "${this.serverless.service.service}" for stage "${this.serverless .getProvider('aws') - .getStage()} ${style.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}` + .getStage()}" ${style.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}` ); - log.info(); // Ensure gap between verbose logging + log.notice(); } }, 'before:package:cleanup': () => mainProgress.notice('Packaging', { isMainEvent: true }), @@ -122,8 +122,7 @@ class AwsPackage { 'finalize': () => { if (this.serverless.processedInput.commands.join(' ') === 'package') { - log.notice(); - log.notice.success( + log.success( `Service packaged ${style.aside( `(${Math.floor( (Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000 diff --git a/lib/plugins/aws/remove/index.js b/lib/plugins/aws/remove/index.js index f47e4de05..34189948e 100644 --- a/lib/plugins/aws/remove/index.js +++ b/lib/plugins/aws/remove/index.js @@ -31,11 +31,11 @@ class AwsRemove { 'initialize': async () => { if (this.serverless.processedInput.commands.join(' ') === 'remove') { log.notice( - `Removing ${this.serverless.service.service} from stage ${this.serverless + `Removing "${this.serverless.service.service}" from stage "${this.serverless .getProvider('aws') - .getStage()} ${style.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}` + .getStage()}" ${style.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}` ); - log.info(); // Ensure gap between verbose logging + log.notice(); // Ensure gap between verbose logging // This is used to ensure that for `remove` command, the `accountId` will be resolved and available // for `generatePayload` telemetry logic @@ -47,6 +47,7 @@ class AwsRemove { ); } }, + 'remove:remove': async () => { const doesEcrRepositoryExistPromise = this.checkIfEcrRepositoryExists(); await this.validate(); @@ -60,10 +61,10 @@ class AwsRemove { await this.removeEcrRepository(); } }, + 'finalize': async () => { if (this.serverless.processedInput.commands.join(' ') !== 'remove') return; - log.notice(); - log.notice.success( + log.success( `Service ${this.serverless.service.service} has been successfully removed ${style.aside( `(${Math.floor( (Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000 diff --git a/lib/plugins/aws/rollback.js b/lib/plugins/aws/rollback.js index 18f582ab2..7f00de91d 100644 --- a/lib/plugins/aws/rollback.js +++ b/lib/plugins/aws/rollback.js @@ -72,7 +72,7 @@ class AwsRollback { )}` ); } else { - log.notice.skip( + log.aside( `No updates to be performed. Rollback skipped. ${style.aside( `(${Math.floor( (Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000 diff --git a/lib/plugins/config.js b/lib/plugins/config.js deleted file mode 100644 index b7e8c7123..000000000 --- a/lib/plugins/config.js +++ /dev/null @@ -1,42 +0,0 @@ -import ServerlessError from '../serverless-error.js'; -import cliCommandsSchema from '../cli/commands-schema.js'; -import utils from '@serverlessinc/sf-core/src/utils.js'; - -const { log } = utils; - -// class wide constants -const validProviders = new Set(['aws']); - -const humanReadableProvidersList = `"${Array.from(validProviders)}"`; - -class Config { - constructor(serverless, options) { - this.serverless = serverless; - this.options = options; - - this.commands = { - config: { - ...cliCommandsSchema.get('config'), - commands: { - credentials: { - // Command defined in AWS context - validProviders, - }, - }, - }, - }; - - this.hooks = { - 'config:config': async () => this.updateConfig(), - 'before:config:credentials:config': () => this.validate(), - }; - } - - // Deprecated in V4 - validate() {} - - // Deprecated in V4 - async updateConfig() {} -} - -export default Config; diff --git a/lib/plugins/index.js b/lib/plugins/index.js index 9794d1e95..d32ee1a5d 100644 --- a/lib/plugins/index.js +++ b/lib/plugins/index.js @@ -1,7 +1,5 @@ async function loadModules() { return Promise.all([ - import('./config.js'), - import('./install.js'), import('./package/package.js'), import('./deploy.js'), import('./invoke.js'), @@ -15,7 +13,6 @@ async function loadModules() { import('./plugin/plugin.js'), import('./plugin/list.js'), import('./plugin/search.js'), - import('./aws/config-credentials.js'), import('./aws/provider.js'), import('./aws/common/index.js'), import('./aws/package/index.js'), diff --git a/lib/plugins/install.js b/lib/plugins/install.js deleted file mode 100644 index 0dc348d2a..000000000 --- a/lib/plugins/install.js +++ /dev/null @@ -1,44 +0,0 @@ -import cliCommandsSchema from '../cli/commands-schema.js'; -import download from '../utils/download-template-from-repo.js'; -import utils from '@serverlessinc/sf-core/src/utils.js'; - -const { log, progress, style } = utils; - -class Install { - constructor(serverless, options) { - this.serverless = serverless; - this.options = options; - - this.commands = { - install: { - ...cliCommandsSchema.get('install'), - }, - }; - - this.hooks = { - 'install:install': async () => this.install(), - }; - } - - async install() { - const commandRunStartTime = Date.now(); - progress.get('main').notice(`Downloading service from provided url: ${this.options.url}`); - const serviceName = await download.downloadTemplateFromRepo( - this.options.url, - this.options.name - ); - const message = [ - `Successfully installed "${serviceName}" `, - `${ - this.options.name && this.options.name !== serviceName ? `as "${this.options.name}"` : '' - }`, - ].join(''); - - log.notice(); - log.notice.success( - `${message} ${style.aside(`(${Math.floor((Date.now() - commandRunStartTime) / 1000)}s)`)}` - ); - } -} - -export default Install; diff --git a/lib/plugins/package/lib/zip-service.js b/lib/plugins/package/lib/zip-service.js index 69401cfce..3a78b4e6a 100644 --- a/lib/plugins/package/lib/zip-service.js +++ b/lib/plugins/package/lib/zip-service.js @@ -3,7 +3,8 @@ import archiver from 'archiver'; import os from 'os'; import path from 'path'; import crypto from 'crypto'; -import fs from 'fs/promises'; +import fs from 'fs'; +import fsp from 'fs/promises'; import { promisify } from 'util'; import * as child from 'child_process'; import globby from 'globby'; @@ -11,6 +12,10 @@ import _ from 'lodash'; import ServerlessError from '../../../serverless-error.js'; import utils from '@serverlessinc/sf-core/src/utils.js'; +const { + createWriteStream, + unlinkSync, +} = fs; const childProcess = promisify(child.exec); const { log } = utils; @@ -73,8 +78,7 @@ export default { // Create artifact in temp path and move it to the package path (if any) later const artifactFilePath = path.join(this.serverless.serviceDir, '.serverless', zipFileName); this.serverless.utils.writeFileDir(artifactFilePath); - - const output = fs.createWriteStream(artifactFilePath); + const output = createWriteStream(artifactFilePath); return new BbPromise((resolve, reject) => { output.on('close', () => resolve(artifactFilePath)); @@ -116,7 +120,7 @@ export default { return BbPromise.all([ // Get file contents and stat in parallel this.getFileContent(fullPath), - fs.statAsync(fullPath), + fsp.stat(fullPath), ]).then( (result) => ({ data: result[0], @@ -134,7 +138,7 @@ export default { // Useful point of entry for e.g. transpilation plugins getFileContent(fullPath) { - return fs.readFileAsync(fullPath); + return fsp.readFile(fullPath); }, }; @@ -206,8 +210,8 @@ async function excludeNodeDevDependencies(serviceDir) { .then(() => BbPromise.mapSeries(['dev', 'prod'], (env) => { const depFile = env === 'dev' ? nodeDevDepFile : nodeProdDepFile; - return fs - .readFileAsync(depFile) + return fsp + .readFile(depFile) .then((fileContent) => _.uniq(fileContent.toString('utf8').split(/[\r\n]+/)).filter(Boolean) ) @@ -229,7 +233,7 @@ async function excludeNodeDevDependencies(serviceDir) { .filter((item) => item.length > 0 && item.match(nodeModulesRegex)) .reduce((globs, item) => { const packagePath = path.join(serviceDir, item, 'package.json'); - return fs.readFileAsync(packagePath, 'utf-8').then( + return fsp.readFile(packagePath, 'utf-8').then( (packageJsonFile) => { const lastIndex = item.lastIndexOf(path.sep) + 1; const moduleName = item.substr(lastIndex); @@ -265,8 +269,8 @@ async function excludeNodeDevDependencies(serviceDir) { }) .then(() => { // cleanup - fs.unlinkSync(nodeDevDepFile); - fs.unlinkSync(nodeProdDepFile); + unlinkSync(nodeDevDepFile); + unlinkSync(nodeProdDepFile); return exAndIn; }) .catch(() => exAndIn) diff --git a/lib/plugins/plugin/lib/utils.js b/lib/plugins/plugin/lib/utils.js index 2e6f623c0..dd9c622c2 100644 --- a/lib/plugins/plugin/lib/utils.js +++ b/lib/plugins/plugin/lib/utils.js @@ -47,7 +47,7 @@ export default { 'It will be automatically downloaded and added to package.json and serverless.yml' ); } else { - log.notice.skip('There are no plugins available to display'); + log.aside('There are no plugins available to display'); } return BbPromise.resolve(); diff --git a/lib/serverless-error.js b/lib/serverless-error.js index 98b11b5c2..085689a5e 100644 --- a/lib/serverless-error.js +++ b/lib/serverless-error.js @@ -2,7 +2,6 @@ class ServerlessError extends Error { constructor(message, code, options = {}) { super(message); this.code = code; - this.decoratedMessage = options.decoratedMessage; } }