Skip to content

Commit add7a6d

Browse files
author
Lukas Holzer
authored
feat: report differences for new framework detection (#5643)
* feat: report differences for new framework detection * chore: pr feedback * chore: pr feedback from japheth
1 parent 5fd6905 commit add7a6d

File tree

7 files changed

+259
-20
lines changed

7 files changed

+259
-20
lines changed

functions/error-reporting.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const handler: Handler = async ({ body }) => {
1616
cause,
1717
cliVersion,
1818
message,
19+
metadata = {},
1920
name,
2021
nodejsVersion,
2122
osName,
@@ -24,13 +25,21 @@ export const handler: Handler = async ({ body }) => {
2425
user,
2526
} = JSON.parse(body)
2627
Bugsnag.notify({ name, message, stack, cause }, (event) => {
28+
event.app = {
29+
releaseStage: 'production',
30+
version: cliVersion,
31+
type: 'netlify-cli',
32+
}
33+
// eslint-disable-next-line fp/no-loops
34+
for (const [section, values] of Object.entries(metadata)) {
35+
event.addMetadata(section, values as Record<string, any>)
36+
}
2737
event.setUser(user.id, user.email, user.name)
2838
event.severity = severity
2939
event.device = {
3040
osName,
3141
runtimeVersions: {
32-
nodejs: nodejsVersion,
33-
cli: cliVersion,
42+
node: nodejsVersion,
3443
},
3544
}
3645
})

package-lock.json

+134
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"@bugsnag/js": "^7.20.0",
8080
"@fastify/static": "^6.6.0",
8181
"@netlify/build": "^29.9.2",
82+
"@netlify/build-info": "^7.0.0-pre-20230418.0",
8283
"@netlify/config": "^20.3.7",
8384
"@netlify/edge-bundler": "^8.13.2",
8485
"@netlify/framework-info": "^9.8.5",

src/commands/dev/dev.mjs

+6-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,12 @@ const dev = async (options, command) => {
111111
/** @type {Partial<import('../../utils/types').ServerSettings>} */
112112
let settings = {}
113113
try {
114-
settings = await detectServerSettings(devConfig, options, site.root)
114+
settings = await detectServerSettings(devConfig, options, site.root, {
115+
site: {
116+
id: site.id,
117+
url: siteUrl,
118+
},
119+
})
115120

116121
cachedConfig.config = getConfigWithPlugins(cachedConfig.config, settings)
117122
} catch (error_) {

src/utils/detect-server-settings.mjs

+94-11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import { EOL } from 'os'
44
import path from 'path'
55
import process from 'process'
66

7+
import { Project } from '@netlify/build-info'
8+
// eslint-disable-next-line import/extensions, n/no-missing-import
9+
import { NodeFS } from '@netlify/build-info/node'
710
import { getFramework, listFrameworks } from '@netlify/framework-info'
811
import fuzzy from 'fuzzy'
912
import getPort from 'get-port'
@@ -12,6 +15,7 @@ import isPlainObject from 'is-plain-obj'
1215
import { NETLIFYDEVWARN, chalk, log } from './command-helpers.mjs'
1316
import { acquirePort } from './dev.mjs'
1417
import { getInternalFunctionsDir } from './functions/functions.mjs'
18+
import { reportError } from './telemetry/report-error.mjs'
1519

1620
const formatProperty = (str) => chalk.magenta(`'${str}'`)
1721
const formatValue = (str) => chalk.green(`'${str}'`)
@@ -112,10 +116,10 @@ const getStaticServerPort = async ({ devConfig }) => {
112116
/**
113117
*
114118
* @param {object} param0
115-
* @param {import('../commands/dev/types').DevConfig} param0.devConfig
119+
* @param {import('../commands/dev/types.js').DevConfig} param0.devConfig
116120
* @param {import('commander').OptionValues} param0.options
117121
* @param {string} param0.projectDir
118-
* @returns {Promise<import('./types').BaseServerSettings>}
122+
* @returns {Promise<import('./types.js').BaseServerSettings>}
119123
*/
120124
const handleStaticServer = async ({ devConfig, options, projectDir }) => {
121125
validateNumberProperty({ devConfig, property: 'staticServerPort' })
@@ -152,8 +156,8 @@ const handleStaticServer = async ({ devConfig, options, projectDir }) => {
152156

153157
/**
154158
* Retrieves the settings from a framework
155-
* @param {import('./types').FrameworkInfo} framework
156-
* @returns {import('./types').BaseServerSettings}
159+
* @param {import('./types.js').FrameworkInfo} framework
160+
* @returns {import('./types.js').BaseServerSettings}
157161
*/
158162
const getSettingsFromFramework = (framework) => {
159163
const {
@@ -182,6 +186,71 @@ const getSettingsFromFramework = (framework) => {
182186

183187
const hasDevCommand = (framework) => Array.isArray(framework.dev.commands) && framework.dev.commands.length !== 0
184188

189+
/**
190+
* The new build setting detection with build systems and frameworks combined
191+
* @param {string} projectDir
192+
*/
193+
const detectSettings = async (projectDir) => {
194+
const fs = new NodeFS()
195+
const project = new Project(fs, projectDir)
196+
197+
return await project.getBuildSettings()
198+
}
199+
200+
/**
201+
*
202+
* @param {import('./types.js').BaseServerSettings | undefined} frameworkSettings
203+
* @param {import('@netlify/build-info').Settings[]} newSettings
204+
* @param {Record<string, Record<string, any>>} [metadata]
205+
*/
206+
const detectChangesInNewSettings = (frameworkSettings, newSettings, metadata) => {
207+
/** @type {string[]} */
208+
const message = ['']
209+
const [setting] = newSettings
210+
211+
if (frameworkSettings?.framework !== setting?.framework) {
212+
message.push(
213+
`- Framework does not match:`,
214+
` [old]: ${frameworkSettings?.framework}`,
215+
` [new]: ${setting?.framework}`,
216+
'',
217+
)
218+
}
219+
220+
if (frameworkSettings?.command !== setting?.devCommand) {
221+
message.push(
222+
`- command does not match:`,
223+
` [old]: ${frameworkSettings?.command}`,
224+
` [new]: ${setting?.devCommand}`,
225+
'',
226+
)
227+
}
228+
229+
if (frameworkSettings?.dist !== setting?.dist) {
230+
message.push(`- dist does not match:`, ` [old]: ${frameworkSettings?.dist}`, ` [new]: ${setting?.dist}`, '')
231+
}
232+
233+
if (frameworkSettings?.frameworkPort !== setting?.frameworkPort) {
234+
message.push(
235+
`- frameworkPort does not match:`,
236+
` [old]: ${frameworkSettings?.frameworkPort}`,
237+
` [new]: ${setting?.frameworkPort}`,
238+
'',
239+
)
240+
}
241+
242+
if (message.length !== 0) {
243+
reportError(
244+
{
245+
name: 'NewSettingsDetectionMismatch',
246+
errorMessage: 'New Settings detection does not match old one',
247+
message: message.join('\n'),
248+
},
249+
{ severity: 'info', metadata },
250+
)
251+
}
252+
}
253+
185254
const detectFrameworkSettings = async ({ projectDir }) => {
186255
const projectFrameworks = await listFrameworks({ projectDir })
187256
const frameworks = projectFrameworks.filter((framework) => hasDevCommand(framework))
@@ -224,7 +293,7 @@ const hasCommandAndTargetPort = ({ devConfig }) => devConfig.command && devConfi
224293
/**
225294
* Creates settings for the custom framework
226295
* @param {*} param0
227-
* @returns {import('./types').BaseServerSettings}
296+
* @returns {import('./types.js').BaseServerSettings}
228297
*/
229298
const handleCustomFramework = ({ devConfig }) => {
230299
if (!hasCommandAndTargetPort({ devConfig })) {
@@ -271,7 +340,7 @@ const mergeSettings = async ({ devConfig, frameworkSettings = {} }) => {
271340
/**
272341
* Handles a forced framework and retrieves the settings for it
273342
* @param {*} param0
274-
* @returns {Promise<import('./types').BaseServerSettings>}
343+
* @returns {Promise<import('./types.js').BaseServerSettings>}
275344
*/
276345
const handleForcedFramework = async ({ devConfig, projectDir }) => {
277346
// this throws if `devConfig.framework` is not a supported framework
@@ -281,15 +350,16 @@ const handleForcedFramework = async ({ devConfig, projectDir }) => {
281350

282351
/**
283352
* Get the server settings based on the flags and the devConfig
284-
* @param {import('../commands/dev/types').DevConfig} devConfig
353+
* @param {import('../commands/dev/types.js').DevConfig} devConfig
285354
* @param {import('commander').OptionValues} options
286355
* @param {string} projectDir
287-
* @returns {Promise<import('./types').ServerSettings>}
356+
* @param {Record<string, Record<string, any>>} [metadata]
357+
* @returns {Promise<import('./types.js').ServerSettings>}
288358
*/
289-
const detectServerSettings = async (devConfig, options, projectDir) => {
359+
const detectServerSettings = async (devConfig, options, projectDir, metadata) => {
290360
validateStringProperty({ devConfig, property: 'framework' })
291361

292-
/** @type {Partial<import('./types').BaseServerSettings>} */
362+
/** @type {Partial<import('./types.js').BaseServerSettings>} */
293363
let settings = {}
294364

295365
if (options.dir || devConfig.framework === '#static') {
@@ -300,6 +370,19 @@ const detectServerSettings = async (devConfig, options, projectDir) => {
300370

301371
const runDetection = !hasCommandAndTargetPort({ devConfig })
302372
const frameworkSettings = runDetection ? await detectFrameworkSettings({ projectDir }) : undefined
373+
const newSettings = runDetection ? await detectSettings(projectDir) : undefined
374+
375+
// just report differences in the settings
376+
detectChangesInNewSettings(frameworkSettings, newSettings || [], {
377+
...metadata,
378+
settings: {
379+
projectDir,
380+
devConfig,
381+
options,
382+
old: frameworkSettings,
383+
settings: newSettings,
384+
},
385+
})
303386

304387
if (frameworkSettings === undefined && runDetection) {
305388
log(`${NETLIFYDEVWARN} No app server detected. Using simple static server`)
@@ -368,7 +451,7 @@ const formatSettingsArrForInquirer = function (frameworks) {
368451
* Returns a copy of the provided config with any plugins provided by the
369452
* server settings
370453
* @param {*} config
371-
* @param {Partial<import('./types').ServerSettings>} settings
454+
* @param {Partial<import('./types.js').ServerSettings>} settings
372455
* @returns {*} Modified config
373456
*/
374457
export const getConfigWithPlugins = (config, settings) => {

0 commit comments

Comments
 (0)