Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure generation and related utils are sync #9692

Merged
merged 1 commit into from
Jul 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/Serverless.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ class Serverless {
);
}
if (this.isLocallyInstalled) return;
const localServerlessPath = await resolveLocalServerlessPath();
const localServerlessPath = resolveLocalServerlessPath();
if (!localServerlessPath) return;
if (localServerlessPath === serverlessPath) {
this.isLocallyInstalled = true;
Expand Down
4 changes: 2 additions & 2 deletions lib/classes/PluginManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -615,8 +615,8 @@ class PluginManager {
// TODO: Remove this logic with `v3.0.0` along with removal of `isTelemetryReportedExternally`
// After we are ensured that local fallback from `v3` will always fall back to `v3` local installation
if (!this.serverless.isTelemetryReportedExternally && !isTelemetryDisabled) {
await storeTelemetryLocally(
await generateTelemetryPayload({
storeTelemetryLocally(
generateTelemetryPayload({
...resolveCliInput(),
serviceDir: this.serverless.serviceDir,
configuration: this.serverless.configurationInput,
Expand Down
10 changes: 5 additions & 5 deletions lib/cli/handle-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ module.exports = async (exception, options = {}) => {

// If provided serverless instance is a local fallback, and we're not in context of it
// Pass error handling to this local fallback implementation
if (isInvokedByGlobalInstallation && !(await resolveIsLocallyInstalled())) {
const localServerlessPath = await resolveLocalServerlessPath();
if (isInvokedByGlobalInstallation && !resolveIsLocallyInstalled()) {
const localServerlessPath = resolveLocalServerlessPath();

try {
// Attempt to use error handler from local version
Expand Down Expand Up @@ -135,7 +135,7 @@ module.exports = async (exception, options = {}) => {
const installationModePostfix = await (async () => {
if (isStandaloneExecutable) return ' (standalone)';
if (isLocallyInstalled != null) return isLocallyInstalled ? ' (local)' : '';
return (await resolveIsLocallyInstalled()) ? ' (local)' : '';
return resolveIsLocallyInstalled() ? ' (local)' : '';
})();
consoleLog(
chalk.yellow(` Framework Version: ${slsVersion}${installationModePostfix}`)
Expand Down Expand Up @@ -175,7 +175,7 @@ module.exports = async (exception, options = {}) => {

if (commandSchema) {
// Report only for recognized commands
const telemetryPayload = await generateTelemetryPayload({
const telemetryPayload = generateTelemetryPayload({
command,
options: cliOptions,
commandSchema,
Expand All @@ -192,7 +192,7 @@ module.exports = async (exception, options = {}) => {
if (!isUserError || !exception.code || !isErrorCodeNormative(exception.code)) {
failureReason.location = resolveErrorLocation(exceptionTokens);
}
await storeTelemetryLocally({ ...telemetryPayload, failureReason, outcome: 'failure' });
storeTelemetryLocally({ ...telemetryPayload, failureReason, outcome: 'failure' });
await sendTelemetry();
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/cli/render-version.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const ServerlessError = require('../serverless-error');
const serverlessPath = path.resolve(__dirname, '../..');

module.exports = async () => {
const localServerlessPath = await resolveLocalServerlessPath();
const localServerlessPath = resolveLocalServerlessPath();

if (localServerlessPath) {
// If the version is already local, do not try to fallback for version resolution to avoid falling into the loop
Expand Down
25 changes: 11 additions & 14 deletions lib/cli/resolve-local-serverless-path.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@

const path = require('path');
const memoizee = require('memoizee');
const resolve = require('ncjsm/resolve');
const resolveSync = require('ncjsm/resolve/sync');

module.exports = memoizee(
async () => {
try {
return path.resolve(
path.dirname((await resolve(process.cwd(), 'serverless')).realPath),
'..'
);
} catch {
return null;
}
},
{ promise: true }
);
// This method should be kept as sync. The reason for it is the fact that
// telemetry generation and persistence needs to be run in sync manner
// and it depends on this function, either directly or indirectly.
module.exports = memoizee(() => {
try {
return path.resolve(path.dirname(resolveSync(process.cwd(), 'serverless').realPath), '..');
} catch {
return null;
}
});
2 changes: 1 addition & 1 deletion lib/plugins/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class Config {
throw new ServerlessError(noSupportErrorMessage, 'AUTO_UPDATE_NOT_SUPPORTED');
}
} else {
if (!(await isNpmGlobalPackage())) {
if (!isNpmGlobalPackage()) {
throw new ServerlessError(noSupportErrorMessage, 'AUTO_UPDATE_NOT_SUPPORTED');
}
if (!(await isNpmPackageWritable())) {
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/eventuallyUpdate.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ module.exports = async (serverless) => {
if (serverless.isLocallyInstalled) return;
if (serverless.isStandaloneExecutable) {
if (process.platform === 'win32') return;
} else if (!(await isNpmGlobalPackage())) {
} else if (!isNpmGlobalPackage()) {
return;
}
const currentVersionData = semver.parse(currentVersion);
Expand Down
7 changes: 5 additions & 2 deletions lib/utils/is-locally-installed.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ const resolveLocalServerlessPath = require('../cli/resolve-local-serverless-path

const serverlessPath = path.resolve(__dirname, '../..');

module.exports = async () => {
const localServerlessPath = await resolveLocalServerlessPath();
// This method should be kept as sync. The reason for it is the fact that
// telemetry generation and persistence needs to be run in sync manner
// and it depends on this function, either directly or indirectly.
module.exports = () => {
const localServerlessPath = resolveLocalServerlessPath();
return serverlessPath === localServerlessPath;
};
30 changes: 15 additions & 15 deletions lib/utils/npmPackage/isGlobal.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@

const memoizee = require('memoizee');
const path = require('path');
const spawn = require('child-process-ext/spawn');
const { spawnSync } = require('child_process');

const serverlessPackageRoot = path.resolve(__dirname, '../../../');

module.exports = memoizee(
async () => {
const npmPackagesRoot = await (async () => {
try {
return String((await spawn('npm', ['root', '-g'])).stdoutBuffer).trim();
} catch {
return null;
}
})();
if (!npmPackagesRoot) return false;
return path.resolve(npmPackagesRoot, 'serverless') === serverlessPackageRoot;
},
{ type: 'promise' }
);
// This method should be kept as sync. The reason for it is the fact that
// telemetry generation and persistence needs to be run in sync manner
// and it depends on this function, either directly or indirectly.
module.exports = memoizee(() => {
const npmPackagesRoot = (() => {
try {
return String(spawnSync('npm', ['root', '-g']).stdout).trim();
} catch {
return null;
}
})();
if (!npmPackagesRoot) return false;
return path.resolve(npmPackagesRoot, 'serverless') === serverlessPackageRoot;
});
44 changes: 22 additions & 22 deletions lib/utils/telemetry/generatePayload.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

const path = require('path');
const os = require('os');
const fs = require('fs').promises;
const resolve = require('ncjsm/resolve');
const fs = require('fs');
const resolveSync = require('ncjsm/resolve/sync');
const _ = require('lodash');
const isPlainObject = require('type/plain-object/is');
const isObject = require('type/object/is');
Expand All @@ -19,11 +19,11 @@ const AWS = require('aws-sdk');

const configValidationModeValues = new Set(['off', 'warn', 'error']);

const checkIsTabAutocompletionInstalled = async () => {
const checkIsTabAutocompletionInstalled = () => {
try {
return (await fs.readdir(path.resolve(os.homedir(), '.config/tabtab'))).some((filename) =>
filename.startsWith('serverless.')
);
return fs
.readdirSync(path.resolve(os.homedir(), '.config/tabtab'))
.some((filename) => filename.startsWith('serverless.'));
} catch {
return false;
}
Expand Down Expand Up @@ -124,7 +124,9 @@ const getServiceConfig = ({ configuration, options }) => {
return result;
};

module.exports = async ({
// This method is explicitly kept as synchronous. The reason for it being the fact that it needs to
// be executed in such manner due to its use in `process.on('SIGINT')` handler.
module.exports = ({
command,
options,
commandSchema,
Expand Down Expand Up @@ -176,39 +178,37 @@ module.exports = async ({
return userConfig.get('userId');
})();

const isLocallyInstalled = await (async () => {
const isLocallyInstalled = (() => {
if (serverless) {
return serverless.isLocallyInstalled;
}

return resolveIsLocallyInstalled();
})();

const usedVersions = await (async () => {
if (!isLocallyInstalled || (await resolveIsLocallyInstalled())) {
const usedVersions = (() => {
if (!isLocallyInstalled || resolveIsLocallyInstalled()) {
return {
'serverless': require('../../../package').version,
'@serverless/dashboard-plugin': require('@serverless/dashboard-plugin/package').version,
};
}
const localServerlessPath = await resolveLocalServerlessPath();
const localServerlessPath = resolveLocalServerlessPath();
return {
'serverless': require(path.resolve(localServerlessPath, 'package.json')).version,
// Since v2.42.0 it's "@serverless/dashboard-plugin"
'@serverless/dashboard-plugin': await (async () => {
'@serverless/dashboard-plugin': (() => {
try {
return require((
await resolve(localServerlessPath, '@serverless/dashboard-plugin/package')
).realPath).version;
return require(resolveSync(localServerlessPath, '@serverless/dashboard-plugin/package')
.realPath).version;
} catch {
return undefined;
}
})(),
'@serverless/enterprise-plugin': await (async () => {
'@serverless/enterprise-plugin': (() => {
try {
return require((
await resolve(localServerlessPath, '@serverless/enterprise-plugin/package')
).realPath).version;
return require(resolveSync(localServerlessPath, '@serverless/enterprise-plugin/package')
.realPath).version;
} catch {
return undefined;
}
Expand All @@ -230,19 +230,19 @@ module.exports = async ({
},
firstLocalInstallationTimestamp: userConfig.get('meta.created_at'),
frameworkLocalUserId: userConfig.get('frameworkId'),
installationType: await (async () => {
installationType: (() => {
if (isStandalone) {
if (process.platform === 'win32') return 'global:standalone:windows';
return 'global:standalone:other';
}
if (!isLocallyInstalled) {
return (await isNpmGlobal()) ? 'global:npm' : 'global:other';
return isNpmGlobal() ? 'global:npm' : 'global:other';
}
if (serverless && serverless.isInvokedByGlobalInstallation) return 'local:fallback';
return 'local:direct';
})(),
isAutoUpdateEnabled: Boolean(userConfig.get('autoUpdate.enabled')),
isTabAutocompletionInstalled: await checkIsTabAutocompletionInstalled(),
isTabAutocompletionInstalled: checkIsTabAutocompletionInstalled(),
timestamp: Date.now(),
timezone,
triggeredDeprecations: Array.from(triggeredDeprecations),
Expand Down
10 changes: 6 additions & 4 deletions lib/utils/telemetry/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,23 @@ async function request(payload, { ids, timeout } = {}) {
return processResponseBody(response, ids, startTime);
}

async function storeLocally(payload, options = {}) {
// This method is explicitly kept as synchronous. The reason for it being the fact that it needs to
// be executed in such manner due to its use in `process.on('SIGINT')` handler.
function storeLocally(payload, options = {}) {
ensurePlainObject(payload);
if (!telemetryUrl) return null;
const isForced = options && options.isForced;
if (isTelemetryDisabled && !isForced) return null;
if (!cacheDirPath) return null;
const id = uuid();

return (async function self() {
return (function self() {
try {
return await fse.writeJson(join(cacheDirPath, id), { payload, timestamp: Date.now() });
return fse.writeJsonSync(join(cacheDirPath, id), { payload, timestamp: Date.now() });
} catch (error) {
if (error.code === 'ENOENT') {
try {
await fse.ensureDir(cacheDirPath);
fse.ensureDirSync(cacheDirPath);
return self();
} catch (ensureDirError) {
logError('Cache dir creation error:', ensureDirError);
Expand Down
62 changes: 30 additions & 32 deletions scripts/postinstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,35 @@ const truthyStr = (val) => val && !['0', 'false', 'f', 'n', 'no'].includes(val.t
const { CI, ADBLOCK, SILENT } = process.env;
const isNpmGlobalPackage = require('../lib/utils/npmPackage/isGlobal');

(async () => {
if (!truthyStr(CI) && !truthyStr(ADBLOCK) && !truthyStr(SILENT)) {
const messageTokens = ['Serverless Framework successfully installed!'];

if (isStandaloneExecutable && !isWindows) {
messageTokens.push(
'To start your first project, please open another terminal and run “serverless”.'
);
} else {
messageTokens.push('To start your first project run “serverless”.');
}

if ((isStandaloneExecutable && !isWindows) || (await isNpmGlobalPackage())) {
messageTokens.push('Turn on automatic updates by running “serverless config --autoupdate”.');
}

if (isStandaloneExecutable && !isWindows) {
messageTokens.push('Uninstall at any time by running “serverless uninstall”.');
}

const message = messageTokens.join('\n\n');
process.stdout.write(
`${
isStandaloneExecutable && isWindows
? message
: boxen(chalk.yellow(message), {
padding: 1,
margin: 1,
borderColor: 'yellow',
})
}\n`
if (!truthyStr(CI) && !truthyStr(ADBLOCK) && !truthyStr(SILENT)) {
const messageTokens = ['Serverless Framework successfully installed!'];

if (isStandaloneExecutable && !isWindows) {
messageTokens.push(
'To start your first project, please open another terminal and run “serverless”.'
);
} else {
messageTokens.push('To start your first project run “serverless”.');
}

if ((isStandaloneExecutable && !isWindows) || isNpmGlobalPackage()) {
messageTokens.push('Turn on automatic updates by running “serverless config --autoupdate”.');
}
})();

if (isStandaloneExecutable && !isWindows) {
messageTokens.push('Uninstall at any time by running “serverless uninstall”.');
}

const message = messageTokens.join('\n\n');
process.stdout.write(
`${
isStandaloneExecutable && isWindows
? message
: boxen(chalk.yellow(message), {
padding: 1,
margin: 1,
borderColor: 'yellow',
})
}\n`
);
}