mirror of
https://github.com/serverless/serverless.git
synced 2026-01-25 15:07:39 +00:00
chore: WIP
This commit is contained in:
parent
3a6dc67a80
commit
2ed4ff6fd5
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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;
|
||||
@ -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)`
|
||||
)}`
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
},
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
@ -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'),
|
||||
|
||||
@ -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;
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -2,7 +2,6 @@ class ServerlessError extends Error {
|
||||
constructor(message, code, options = {}) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.decoratedMessage = options.decoratedMessage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user