Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: limit scope of changes to runtime files
  • Loading branch information
mrstork committed Oct 14, 2025
commit 1dd2d975ed93ea3319896b521c7117d326e40f49
7 changes: 3 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"@netlify/otel": "^4.3.0",
"@netlify/serverless-functions-api": "^2.5.0",
"@netlify/zip-it-and-ship-it": "^14.1.8",
"@opentelemetry/api": "^1.8.0",
"@playwright/test": "^1.43.1",
"@types/adm-zip": "^0.5.7",
"@types/node": "^20.12.7",
Expand Down
7 changes: 4 additions & 3 deletions src/build/content/prerendered.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { existsSync } from 'node:fs'
import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { join } from 'node:path'

import { getTracer, withActiveSpan } from '@netlify/otel'
import { trace } from '@opentelemetry/api'
import { wrapTracer } from '@opentelemetry/api/experimental'
import { glob } from 'fast-glob'
import type { RouteMetadata } from 'next-with-cache-handler-v2/dist/export/routes/types.js'
import pLimit from 'p-limit'
Expand All @@ -20,7 +21,7 @@ import type {
import type { PluginContext } from '../plugin-context.js'
import { verifyNetlifyForms } from '../verification.js'

const tracer = getTracer('Next runtime')
const tracer = wrapTracer(trace.getTracer('Next runtime'))

/**
* Write a cache entry to the blob upload directory.
Expand Down Expand Up @@ -157,7 +158,7 @@ const buildFetchCacheValue = async (
* Upload prerendered content to the blob store
*/
export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void> => {
return withActiveSpan(tracer, 'copyPrerenderedContent', async () => {
return tracer.withActiveSpan('copyPrerenderedContent', async () => {
try {
// ensure the blob directory exists
await mkdir(ctx.blobDir, { recursive: true })
Expand Down
9 changes: 5 additions & 4 deletions src/build/content/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import { createRequire } from 'node:module'
import { dirname, join, resolve, sep } from 'node:path'
import { join as posixJoin, sep as posixSep } from 'node:path/posix'

import { getTracer, withActiveSpan } from '@netlify/otel'
import { trace } from '@opentelemetry/api'
import { wrapTracer } from '@opentelemetry/api/experimental'
import glob from 'fast-glob'
import type { MiddlewareManifest } from 'next/dist/build/webpack/plugins/middleware-plugin.js'
import type { FunctionsConfigManifest } from 'next-with-cache-handler-v2/dist/build/index.js'
Expand All @@ -23,7 +24,7 @@ import type { RunConfig } from '../../run/config.js'
import { RUN_CONFIG_FILE } from '../../run/constants.js'
import type { PluginContext, RequiredServerFilesManifest } from '../plugin-context.js'

const tracer = getTracer('Next runtime')
const tracer = wrapTracer(trace.getTracer('Next runtime'))

const toPosixPath = (path: string) => path.split(sep).join(posixSep)

Expand All @@ -35,7 +36,7 @@ function isError(error: unknown): error is NodeJS.ErrnoException {
* Copy App/Pages Router Javascript needed by the server handler
*/
export const copyNextServerCode = async (ctx: PluginContext): Promise<void> => {
await withActiveSpan(tracer, 'copyNextServerCode', async () => {
await tracer.withActiveSpan('copyNextServerCode', async () => {
// update the dist directory inside the required-server-files.json to work with
// nx monorepos and other setups where the dist directory is modified
const reqServerFilesPath = join(
Expand Down Expand Up @@ -288,7 +289,7 @@ async function patchNextModules(
}

export const copyNextDependencies = async (ctx: PluginContext): Promise<void> => {
await withActiveSpan(tracer, 'copyNextDependencies', async () => {
await tracer.withActiveSpan('copyNextDependencies', async () => {
const entries = await readdir(ctx.standaloneDir)
const filter = ctx.constants.IS_LOCAL ? undefined : nodeModulesFilter

Expand Down
13 changes: 7 additions & 6 deletions src/build/content/static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@ import { existsSync } from 'node:fs'
import { cp, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'
import { basename, join } from 'node:path'

import { getTracer, withActiveSpan } from '@netlify/otel'
import { trace } from '@opentelemetry/api'
import { wrapTracer } from '@opentelemetry/api/experimental'
import glob from 'fast-glob'

import type { HtmlBlob } from '../../shared/blob-types.cjs'
import { encodeBlobKey } from '../../shared/blobkey.js'
import { PluginContext } from '../plugin-context.js'
import { verifyNetlifyForms } from '../verification.js'

const tracer = getTracer('Next runtime')
const tracer = wrapTracer(trace.getTracer('Next runtime'))

/**
* Assemble the static content for being uploaded to the blob storage
*/
export const copyStaticContent = async (ctx: PluginContext): Promise<void> => {
return withActiveSpan(tracer, 'copyStaticContent', async () => {
return tracer.withActiveSpan('copyStaticContent', async () => {
const srcDir = join(ctx.publishDir, 'server/pages')
const destDir = ctx.blobDir

Expand Down Expand Up @@ -57,7 +58,7 @@ export const copyStaticContent = async (ctx: PluginContext): Promise<void> => {
* Copy static content to the static dir so it is uploaded to the CDN
*/
export const copyStaticAssets = async (ctx: PluginContext): Promise<void> => {
return withActiveSpan(tracer, 'copyStaticAssets', async (span): Promise<void> => {
return tracer.withActiveSpan('copyStaticAssets', async (span): Promise<void> => {
try {
await rm(ctx.staticDir, { recursive: true, force: true })
const { basePath } = await ctx.getRoutesManifest()
Expand All @@ -72,7 +73,7 @@ export const copyStaticAssets = async (ctx: PluginContext): Promise<void> => {
})
}
} catch (error) {
span?.end()
span.end()
ctx.failBuild('Failed copying static assets', error)
}
})
Expand All @@ -93,7 +94,7 @@ export const setHeadersConfig = async (ctx: PluginContext): Promise<void> => {
}

export const copyStaticExport = async (ctx: PluginContext): Promise<void> => {
await withActiveSpan(tracer, 'copyStaticExport', async () => {
await tracer.withActiveSpan('copyStaticExport', async () => {
if (!ctx.exportDetail?.outDirectory) {
ctx.failBuild('Export directory not found')
}
Expand Down
11 changes: 6 additions & 5 deletions src/build/functions/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
import { join, relative } from 'node:path'
import { join as posixJoin } from 'node:path/posix'

import { getTracer, withActiveSpan } from '@netlify/otel'
import { trace } from '@opentelemetry/api'
import { wrapTracer } from '@opentelemetry/api/experimental'
import { glob } from 'fast-glob'

import {
Expand All @@ -12,11 +13,11 @@ import {
} from '../content/server.js'
import { PluginContext, SERVER_HANDLER_NAME } from '../plugin-context.js'

const tracer = getTracer('Next runtime')
const tracer = wrapTracer(trace.getTracer('Next runtime'))

/** Copies the runtime dist folder to the lambda */
const copyHandlerDependencies = async (ctx: PluginContext) => {
await withActiveSpan(tracer, 'copyHandlerDependencies', async (span) => {
await tracer.withActiveSpan('copyHandlerDependencies', async (span) => {
const promises: Promise<void>[] = []
// if the user specified some files to include in the lambda
// we need to copy them to the functions-internal folder
Expand All @@ -30,7 +31,7 @@ const copyHandlerDependencies = async (ctx: PluginContext) => {
posixJoin(ctx.relativeAppDir, '.env.production.local'),
)

span?.setAttribute('next.includedFiles', includedFiles.join(','))
span.setAttribute('next.includedFiles', includedFiles.join(','))

const resolvedFiles = await Promise.all(
includedFiles.map((globPattern) => glob(globPattern, { cwd: process.cwd() })),
Expand Down Expand Up @@ -136,7 +137,7 @@ export const clearStaleServerHandlers = async (ctx: PluginContext) => {
* Create a Netlify function to run the Next.js server
*/
export const createServerHandler = async (ctx: PluginContext) => {
await withActiveSpan(tracer, 'createServerHandler', async () => {
await tracer.withActiveSpan('createServerHandler', async () => {
await mkdir(join(ctx.serverHandlerRuntimeModulesDir), { recursive: true })

await copyNextServerCode(ctx)
Expand Down
2 changes: 1 addition & 1 deletion src/build/skew-protection.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mkdir, writeFile } from 'node:fs/promises'
import { dirname } from 'node:path'

import type { Span } from '@netlify/otel/opentelemetry'
import type { Span } from '@opentelemetry/api'
import { afterEach, beforeEach, describe, expect, it, MockInstance, vi } from 'vitest'

import type { PluginContext } from './plugin-context.js'
Expand Down
6 changes: 3 additions & 3 deletions src/build/skew-protection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mkdir, writeFile } from 'node:fs/promises'
import { dirname } from 'node:path'

import type { Span } from '@netlify/otel/opentelemetry'
import type { Span } from '@opentelemetry/api'

import type { PluginContext } from './plugin-context.js'

Expand Down Expand Up @@ -87,10 +87,10 @@ export function shouldEnableSkewProtection(ctx: PluginContext) {
}
}

export const setSkewProtection = async (ctx: PluginContext, span?: Span) => {
export const setSkewProtection = async (ctx: PluginContext, span: Span) => {
const { enabled, enabledOrDisabledReason } = shouldEnableSkewProtection(ctx)

span?.setAttribute('skewProtection', enabledOrDisabledReason)
span.setAttribute('skewProtection', enabledOrDisabledReason)

if (!enabled) {
if (enabledOrDisabledReason === EnabledOrDisabledReason.OPT_OUT_NO_VALID_DEPLOY_ID_ENV_VAR) {
Expand Down
19 changes: 10 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { rm } from 'fs/promises'

import type { NetlifyPluginOptions } from '@netlify/build'
import { getTracer, withActiveSpan } from '@netlify/otel'
import { trace } from '@opentelemetry/api'
import { wrapTracer } from '@opentelemetry/api/experimental'

import { restoreBuildCache, saveBuildCache } from './build/cache.js'
import { copyPrerenderedContent } from './build/content/prerendered.js'
Expand All @@ -27,15 +28,15 @@ import {
const skipPlugin =
process.env.NETLIFY_NEXT_PLUGIN_SKIP === 'true' || process.env.NETLIFY_NEXT_PLUGIN_SKIP === '1'
const skipText = 'Skipping Next.js plugin due to NETLIFY_NEXT_PLUGIN_SKIP environment variable.'
const tracer = getTracer('Next.js runtime')
const tracer = wrapTracer(trace.getTracer('Next.js runtime'))

export const onPreDev = async (options: NetlifyPluginOptions) => {
if (skipPlugin) {
console.warn(skipText)
return
}

await withActiveSpan(tracer, 'onPreDev', async () => {
await tracer.withActiveSpan('onPreDev', async () => {
const context = new PluginContext(options)

// Blob files left over from `ntl build` interfere with `ntl dev` when working with regional blobs
Expand All @@ -49,7 +50,7 @@ export const onPreBuild = async (options: NetlifyPluginOptions) => {
return
}

await withActiveSpan(tracer, 'onPreBuild', async (span) => {
await tracer.withActiveSpan('onPreBuild', async (span) => {
// Enable Next.js standalone mode at build time
process.env.NEXT_PRIVATE_STANDALONE = 'true'
const ctx = new PluginContext(options)
Expand All @@ -72,12 +73,12 @@ export const onBuild = async (options: NetlifyPluginOptions) => {
return
}

await withActiveSpan(tracer, 'onBuild', async (span) => {
await tracer.withActiveSpan('onBuild', async (span) => {
const ctx = new PluginContext(options)

verifyPublishDir(ctx)

span?.setAttribute('next.buildConfig', JSON.stringify(ctx.buildConfig))
span.setAttribute('next.buildConfig', JSON.stringify(ctx.buildConfig))

// only save the build cache if not run via the CLI
if (!options.constants.IS_LOCAL) {
Expand Down Expand Up @@ -110,7 +111,7 @@ export const onPostBuild = async (options: NetlifyPluginOptions) => {
return
}

await withActiveSpan(tracer, 'onPostBuild', async () => {
await tracer.withActiveSpan('onPostBuild', async () => {
await publishStaticDir(new PluginContext(options))
})
}
Expand All @@ -121,7 +122,7 @@ export const onSuccess = async () => {
return
}

await withActiveSpan(tracer, 'onSuccess', async () => {
await tracer.withActiveSpan('onSuccess', async () => {
const prewarm = [process.env.DEPLOY_URL, process.env.DEPLOY_PRIME_URL, process.env.URL].filter(
// If running locally then the deploy ID is a placeholder value. Filtering for `https://0--` removes it.
(url?: string): url is string => Boolean(url && !url.startsWith('https://0--')),
Expand All @@ -136,7 +137,7 @@ export const onEnd = async (options: NetlifyPluginOptions) => {
return
}

await withActiveSpan(tracer, 'onEnd', async () => {
await tracer.withActiveSpan('onEnd', async () => {
await unpublishStaticDir(new PluginContext(options))
})
}
Loading