From 2550e2dd58ceed7f21e8db9a84683bb418ed5da8 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 12 Oct 2026 16:37:06 +0100 Subject: [PATCH] Address EXDEV issue --- lib/entry-points.js | 15 +++++++++++++-- src/diagnostics.ts | 21 ++++++++++++++++++--- src/util.ts | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index d1cbb8f66..040571991 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -170021,6 +170021,9 @@ function wrapError(error3) { function getErrorMessage(error3) { return error3 instanceof Error ? error3.message : String(error3); } +function isErrnoException(error3) { + return typeof error3 === "object" && error3 !== null && "code" in error3 && typeof error3.code === "string"; +} function prettyPrintPack(pack) { return `${pack.name}${pack.version ? `@${pack.version}` : ""}${pack.path ? `:${pack.path}` : ""}`; } @@ -173099,10 +173102,18 @@ function flushDiagnostics(action, config) { const destination = import_path2.default.join(directory, filename); try { fs7.mkdirSync(directory, { recursive: true }); - fs7.renameSync(temporary.path, destination); + try { + fs7.renameSync(temporary.path, destination); + } catch (renameErr) { + if (!isErrnoException(renameErr) || renameErr.code !== "EXDEV") { + throw renameErr; + } + fs7.copyFileSync(temporary.path, destination); + fs7.unlinkSync(temporary.path); + } } catch (err) { action.logger.warning( - `Failed to rename '${temporary.path}' to '${destination}': ${getErrorMessage(err)}` + `Failed to relocate '${temporary.path}' to '${destination}': ${getErrorMessage(err)}` ); failedFlushDiagnostics.push(temporary); } diff --git a/src/diagnostics.ts b/src/diagnostics.ts index d0083e30e..a3b44fd76 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -6,7 +6,11 @@ import { getTemporaryDirectory } from "./actions-util"; import type { Config } from "./config-utils"; import { Language } from "./languages"; import { getActionsLogger, type Logger } from "./logging"; -import { getCodeQLDatabasePath, getErrorMessage } from "./util"; +import { + getCodeQLDatabasePath, + getErrorMessage, + isErrnoException, +} from "./util"; /** * Known tags for diagnostics. There is currently only "internal-error", @@ -334,10 +338,21 @@ export function flushDiagnostics( try { fs.mkdirSync(directory, { recursive: true }); - fs.renameSync(temporary.path, destination); + try { + fs.renameSync(temporary.path, destination); + } catch (renameErr: unknown) { + // Rethrow the error if it isn't an EXDEV one. + if (!isErrnoException(renameErr) || renameErr.code !== "EXDEV") { + throw renameErr; + } + + // The error was an EXDEV one: try to copy the file and delete the temporary one. + fs.copyFileSync(temporary.path, destination); + fs.unlinkSync(temporary.path); + } } catch (err) { action.logger.warning( - `Failed to rename '${temporary.path}' to '${destination}': ${getErrorMessage(err)}`, + `Failed to relocate '${temporary.path}' to '${destination}': ${getErrorMessage(err)}`, ); failedFlushDiagnostics.push(temporary); diff --git a/src/util.ts b/src/util.ts index 566386de6..2cacd4fbb 100644 --- a/src/util.ts +++ b/src/util.ts @@ -924,6 +924,21 @@ export function getErrorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } +/** + * Approximates whether {@link error} is like a {@link NodeJS.ErrnoException}. + * We only typically care about the `code` property, so that's what we check for here. + */ +export function isErrnoException( + error: unknown, +): error is NodeJS.ErrnoException { + return ( + typeof error === "object" && + error !== null && + "code" in error && + typeof error.code === "string" + ); +} + export function prettyPrintPack(pack: Pack) { return `${pack.name}${pack.version ? `@${pack.version}` : ""}${ pack.path ? `:${pack.path}` : ""