-
Notifications
You must be signed in to change notification settings - Fork 95
fix: incorrect path of middleware nft in setups using base directory #3262
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
fix: incorrect path of middleware nft in setups using base directory #3262
Conversation
📊 Package size report -0%↓
Unchanged files
🤖 This report was automatically generated by pkg-size-action |
| const entry = 'server/middleware.js' | ||
| const nft = `${entry}.nft.json` | ||
| const nftFilesPath = join(process.cwd(), ctx.distDir, nft) | ||
| const nftFilesPath = join(ctx.publishDir, nft) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is second attempt at fixing this path (previous one was in #3211 ).
This time using publishDir as it already should point to absolute path to .next.
We already use publishDir in middleware handling to find relevant manifest files:
opennextjs-netlify/src/build/plugin-context.ts
Lines 262 to 286 in 6451ec3
| /** | |
| * Get Next.js middleware config from the build output | |
| */ | |
| async getMiddlewareManifest(): Promise<MiddlewareManifest> { | |
| return JSON.parse( | |
| await readFile(join(this.publishDir, 'server/middleware-manifest.json'), 'utf-8'), | |
| ) | |
| } | |
| /** | |
| * Get Next.js Functions Config Manifest config if it exists from the build output | |
| */ | |
| async getFunctionsConfigManifest(): Promise<FunctionsConfigManifest | null> { | |
| const functionsConfigManifestPath = join( | |
| this.publishDir, | |
| 'server/functions-config-manifest.json', | |
| ) | |
| if (existsSync(functionsConfigManifestPath)) { | |
| return JSON.parse(await readFile(functionsConfigManifestPath, 'utf-8')) | |
| } | |
| // this file might not have been produced | |
| return null | |
| } |
and since those work (we wouldn't get to code path trying to read
.nft file if they didn't), it's good idea to just reuse this method.
Some explanation why current method is not working in some cases:
In case of monorepos that are configured to use base directory instead of packagePath, the CWD will be workspace and not root of monorepo. The ctx.distDir will be path from monorepo root to .next directory (so will be prefixed with base directory). Both combined means that base directory is repeated in produced final path. Using example with repo that has app workspace and Netlify setup using base directory set to app:
// before
cwd: '/opt/build/repo/app'
distDir: 'app/.next', // <- this is produced using some values from Next.js manifest files, in particular `relativeAppDir` from `required-server-files.json`, but this path is not `base` aware and shouldn't be used if we are reading files outside of `.next/standalone` directory
join(cwd, distDir): '/opt/build/repo/app/app/.next' // <-- duplicated `app` dir leading to fatal "Error: ENOENT: no such file or directory, open '/opt/build/repo/app/app/.next/server/middleware.js.nft.json'" error
// with this change
publishDir: '/opt/build/repo/app/.next' // <- correct path
Note: with last fix attempt we did add smoke fixture using packagePath. In this PR I add another smoke fixture replicating monorepo setup using base directory instead of packagePath. Both are passing with the changes
…d of package path and containing proxy / node middleware
d5b1cac to
a10748f
Compare
| publishDirectory: '.next', | ||
| runtimeInstallationPath: 'app', | ||
| smoke: true, | ||
| useBuildbot: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is annoying because we don't see build failure logs when using buildbot, but I couldn't replicate the exact conditions with CLI deploys.
https://app.netlify.com/projects/next-runtime-testing/deploys/691db94c23e26600e071528e (this might get automatically deleted soon!) is example build reproducing
1:34:41 PM: Error message
1:34:41 PM: Error: ENOENT: no such file or directory, open '/opt/build/repo/app/app/.next/server/middleware.js.nft.json'
kind of errors without any runtime code changes (1st commit) that is fixed by runtime changes (2nd commit)
| 'Content-Type': 'application/zip', | ||
| Authorization: `Bearer ${process.env.NETLIFY_AUTH_TOKEN}`, | ||
| const result = await fetch( | ||
| `https://api.netlify.com/api/v1/sites/${siteId}/builds?clear_cache=true`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added clear_cache=true here. We used buildbot zip builds for skew protection only so far which creates its own temporary sites, so build cache was not a problem.
However with this test we use shared next-runtime-testing site, so we do potentially get a build cache which seems to prevent using npm-packed runtime and instead use auto-installed one without code changes from pull request
| entry.includes('node_modules') || | ||
| entry.includes('.git') || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new fixture has nested directory with node_modules, so this ensures we skip zipping those
| let relativePathToPackage = relative( | ||
| join(isolatedFixtureRoot, siteRelDir), | ||
| join(isolatedFixtureRoot, packageName), | ||
| ) | ||
| if (!relativePathToPackage.startsWith('.')) { | ||
| relativePathToPackage = `./${relativePathToPackage}` | ||
| } | ||
|
|
||
| switch (packageManger) { | ||
| case 'npm': | ||
| command = `npm install --ignore-scripts --no-audit --legacy-peer-deps ${packageName} ${ | ||
| workspaceRelPath ? `-w ${workspaceRelPath}` : '' | ||
| }` | ||
| break | ||
| case 'yarn': | ||
| command = `yarn ${workspaceName ? `workspace ${workspaceName}` : '-W'} add file:${join( | ||
| isolatedFixtureRoot, | ||
| packageName, | ||
| )} --ignore-scripts` | ||
| break | ||
| case 'berry': | ||
| command = `yarn ${workspaceName ? `workspace ${workspaceName}` : ''} add ${join( | ||
| isolatedFixtureRoot, | ||
| packageName, | ||
| )}` | ||
| env['YARN_ENABLE_SCRIPTS'] = 'false' | ||
| break | ||
| case 'pnpm': | ||
| command = `pnpm add file:${join(isolatedFixtureRoot, packageName)} ${ | ||
| command = `pnpm add file:${relativePathToPackage} ${ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
relative path needed for buildbot to actually find tarballed runtime in zip file - this possibly will have to be replicated for other package managers if we will need to use buildbot for those (I only did pnpm here, npm already worked when buildbot builds were added for skew protection)
| @@ -0,0 +1,7 @@ | |||
| [build] | |||
| base = "app" | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most important part of this fixture is using base directory
serhalp
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, good find!
Description
Details in #3262 (review)
Tests
Added test case for monorepo setup that is using base directory instead of package path and use proxy / node middleware