* Resolve the image to run locally: either a configured prebuilt image, or one * built locally from the service's Dockerfile. * * Unlike the cloud build, dev builds for the host architecture (no * `--platform linux/amd64`) and never pushes to a registry.
( options: StartDevServerOptions, out: DevOutput, span?: Span )
| 190 | * `--platform linux/amd64`) and never pushes to a registry. |
| 191 | */ |
| 192 | async function resolveDevImage( |
| 193 | options: StartDevServerOptions, |
| 194 | out: DevOutput, |
| 195 | span?: Span |
| 196 | ): Promise<string> { |
| 197 | const { config, workPath, entrypoint } = options; |
| 198 | |
| 199 | const entrypointRef = readString(entrypoint); |
| 200 | // An entrypoint that names a Dockerfile (including the `Dockerfile.vercel` / |
| 201 | // `Containerfile.vercel` opt-in markers) is built directly. Otherwise — e.g. |
| 202 | // when the `container` framework preset resolves its entrypoint via |
| 203 | // `<detect>` — discover an opt-in marker in the work directory. This mirrors |
| 204 | // the build path (`resolveImageHandler`) so dev and deploy resolve the same |
| 205 | // Dockerfile. |
| 206 | const dockerfileConfigured = |
| 207 | entrypointRef && isDockerfileRef(entrypointRef) |
| 208 | ? entrypointRef |
| 209 | : findDockerfile(workPath); |
| 210 | const dockerfileRel = dockerfileConfigured ?? 'Dockerfile'; |
| 211 | const dockerfilePath = path.join(workPath, dockerfileRel); |
| 212 | const hasDockerfile = |
| 213 | dockerfileConfigured !== undefined || existsSync(dockerfilePath); |
| 214 | |
| 215 | const prebuiltImage = |
| 216 | readString(config.handler) ?? (hasDockerfile ? undefined : entrypointRef); |
| 217 | |
| 218 | if (!hasDockerfile) { |
| 219 | if (!prebuiltImage) { |
| 220 | throw new Error( |
| 221 | 'Container service must specify an entrypoint: a prebuilt OCI image ' + |
| 222 | 'reference, or a Dockerfile path to run with `vercel dev`.' |
| 223 | ); |
| 224 | } |
| 225 | span?.setAttributes({ 'container.dev_mode': 'prebuilt' }); |
| 226 | emit(out, `▲ container vercel dev: using prebuilt image ${prebuiltImage}`); |
| 227 | return prebuiltImage; |
| 228 | } |
| 229 | |
| 230 | if (!existsSync(dockerfilePath)) { |
| 231 | throw new Error( |
| 232 | `Dockerfile not found at "${dockerfilePath}" for container service.` |
| 233 | ); |
| 234 | } |
| 235 | |
| 236 | const serviceName = options.service?.name ?? 'service'; |
| 237 | const tag = devImageTag(serviceName); |
| 238 | const contextDir = path.dirname(dockerfilePath); |
| 239 | |
| 240 | // Forward the project build env as `--build-arg`s so Dockerfile `ARG`s work |
| 241 | // in dev too, matching the cloud build. |
| 242 | const buildArgFlags: string[] = []; |
| 243 | const buildEnv = (options.meta?.buildEnv ?? {}) as Record< |
| 244 | string, |
| 245 | string | undefined |
| 246 | >; |
| 247 | for (const [key, value] of Object.entries(buildEnv)) { |
| 248 | if (typeof value === 'string') { |
| 249 | buildArgFlags.push('--build-arg', `${key}=${value}`); |
no test coverage detected