Use lstat instead of stat

This commit is contained in:
Michael B. Gale
2026-10-19 12:28:54 +01:00
committed by GitHub
parent 54a1bbe878
commit 314a161fec
4 changed files with 97 additions and 37 deletions

10
lib/entry-points.js generated
View File

@@ -175732,7 +175732,15 @@ var XCODE_APP_FILENAME_PATTERN = new RegExp(
);
function xcodeVersion(logger2) {
try {
const stats = import_fs.default.statSync(XCODE_SELECT_LINK_PATH);
const stats = import_fs.default.lstatSync(XCODE_SELECT_LINK_PATH, {
throwIfNoEntry: false
});
if (stats === void 0) {
logger2.debug(
`'${XCODE_SELECT_LINK_PATH}' does not exist. Xcode is probably not installed.`
);
return void 0;
}
if (!stats.isSymbolicLink()) {
logger2.warning(
`${XCODE_SELECT_LINK_PATH} exists, but is not a symbolic link.`

View File

@@ -289,8 +289,8 @@ test.serial(
const env = getTestEnv();
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
const statSync = sinon
.stub(fs, "statSync")
const lstatSync = sinon
.stub(fs, "lstatSync")
.returns({ isSymbolicLink: () => true } as fs.Stats);
const readlinkSync = sinon
.stub(fs, "readlinkSync")
@@ -317,8 +317,11 @@ test.serial(
codeql,
);
t.is(statSync.callCount, 1);
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
t.is(lstatSync.callCount, 1);
t.deepEqual(lstatSync.args[0], [
XCODE_SELECT_LINK_PATH,
{ throwIfNoEntry: false },
]);
t.is(readlinkSync.callCount, 1);
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
@@ -339,8 +342,8 @@ test.serial(
const env = getTestEnv();
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
const statSync = sinon
.stub(fs, "statSync")
const lstatSync = sinon
.stub(fs, "lstatSync")
.returns({ isSymbolicLink: () => true } as fs.Stats);
const readlinkSync = sinon
.stub(fs, "readlinkSync")
@@ -367,8 +370,11 @@ test.serial(
codeql,
);
t.is(statSync.callCount, 1);
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
t.is(lstatSync.callCount, 1);
t.deepEqual(lstatSync.args[0], [
XCODE_SELECT_LINK_PATH,
{ throwIfNoEntry: false },
]);
t.is(readlinkSync.callCount, 1);
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
@@ -409,8 +415,8 @@ test.serial(
const env = getTestEnv();
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
const statSync = sinon
.stub(fs, "statSync")
const lstatSync = sinon
.stub(fs, "lstatSync")
.returns({ isSymbolicLink: () => true } as fs.Stats);
const readlinkSync = sinon
.stub(fs, "readlinkSync")
@@ -442,8 +448,11 @@ test.serial(
),
);
t.is(statSync.callCount, 1);
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
t.is(lstatSync.callCount, 1);
t.deepEqual(lstatSync.args[0], [
XCODE_SELECT_LINK_PATH,
{ throwIfNoEntry: false },
]);
t.is(readlinkSync.callCount, 1);
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);

View File

@@ -74,11 +74,31 @@ test("macOSVersion returns semver if parsing succeeds", (t) => {
test.serial("xcodeVersion returns undefined if symlink doesn't exist", (t) => {
const logger = new RecordingLogger();
const statSync = sinon.stub(fs, "statSync").throws(new Error("ENOENT"));
const lstatSync = sinon.stub(fs, "lstatSync").returns(undefined);
t.is(xcodeVersion(logger), undefined);
t.is(statSync.callCount, 1);
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
t.is(lstatSync.callCount, 1);
t.deepEqual(lstatSync.args[0], [
XCODE_SELECT_LINK_PATH,
{ throwIfNoEntry: false },
]);
checkExpectedLogMessages(t, logger.messages, [
"Xcode is probably not installed.",
]);
});
test.serial("xcodeVersion returns undefined if lstatSync throws", (t) => {
const logger = new RecordingLogger();
const lstatSync = sinon.stub(fs, "lstatSync").throws(new Error("ENOENT"));
t.is(xcodeVersion(logger), undefined);
t.is(lstatSync.callCount, 1);
t.deepEqual(lstatSync.args[0], [
XCODE_SELECT_LINK_PATH,
{ throwIfNoEntry: false },
]);
checkExpectedLogMessages(t, logger.messages, [
"Unable to determine Xcode version: ENOENT",
@@ -88,13 +108,16 @@ test.serial("xcodeVersion returns undefined if symlink doesn't exist", (t) => {
test.serial("xcodeVersion returns undefined if file is not a symlink", (t) => {
const logger = new RecordingLogger();
const statSync = sinon
.stub(fs, "statSync")
const lstatSync = sinon
.stub(fs, "lstatSync")
.returns({ isSymbolicLink: () => false } as fs.Stats);
t.is(xcodeVersion(logger), undefined);
t.is(statSync.callCount, 1);
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
t.is(lstatSync.callCount, 1);
t.deepEqual(lstatSync.args[0], [
XCODE_SELECT_LINK_PATH,
{ throwIfNoEntry: false },
]);
checkExpectedLogMessages(t, logger.messages, [
"exists, but is not a symbolic link",
@@ -106,14 +129,17 @@ test.serial(
(t) => {
const logger = new RecordingLogger();
const statSync = sinon
.stub(fs, "statSync")
const lstatSync = sinon
.stub(fs, "lstatSync")
.returns({ isSymbolicLink: () => true } as fs.Stats);
const readlinkSync = sinon.stub(fs, "readlinkSync").returns("");
t.is(xcodeVersion(logger), undefined);
t.is(statSync.callCount, 1);
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
t.is(lstatSync.callCount, 1);
t.deepEqual(lstatSync.args[0], [
XCODE_SELECT_LINK_PATH,
{ throwIfNoEntry: false },
]);
t.is(readlinkSync.callCount, 1);
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
@@ -128,16 +154,19 @@ test.serial(
(t) => {
const logger = new RecordingLogger();
const statSync = sinon
.stub(fs, "statSync")
const lstatSync = sinon
.stub(fs, "lstatSync")
.returns({ isSymbolicLink: () => true } as fs.Stats);
const readlinkSync = sinon
.stub(fs, "readlinkSync")
.returns("/Applications/Xcode.app/Contents/Developer");
t.is(xcodeVersion(logger), undefined);
t.is(statSync.callCount, 1);
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
t.is(lstatSync.callCount, 1);
t.deepEqual(lstatSync.args[0], [
XCODE_SELECT_LINK_PATH,
{ throwIfNoEntry: false },
]);
t.is(readlinkSync.callCount, 1);
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
@@ -150,16 +179,19 @@ test.serial(
test.serial("xcodeVersion returns undefined if match can't be parsed", (t) => {
const logger = new RecordingLogger();
const statSync = sinon
.stub(fs, "statSync")
const lstatSync = sinon
.stub(fs, "lstatSync")
.returns({ isSymbolicLink: () => true } as fs.Stats);
const readlinkSync = sinon
.stub(fs, "readlinkSync")
.returns("/Applications/Xcode_00.0.app/Contents/Developer");
t.is(xcodeVersion(logger), undefined);
t.is(statSync.callCount, 1);
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
t.is(lstatSync.callCount, 1);
t.deepEqual(lstatSync.args[0], [
XCODE_SELECT_LINK_PATH,
{ throwIfNoEntry: false },
]);
t.is(readlinkSync.callCount, 1);
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
@@ -171,8 +203,8 @@ test.serial("xcodeVersion returns undefined if match can't be parsed", (t) => {
test.serial("xcodeVersion returns version from resolved path", (t) => {
const logger = new RecordingLogger();
const statSync = sinon
.stub(fs, "statSync")
const lstatSync = sinon
.stub(fs, "lstatSync")
.returns({ isSymbolicLink: () => true } as fs.Stats);
const readlinkSync = sinon
.stub(fs, "readlinkSync")
@@ -185,8 +217,11 @@ test.serial("xcodeVersion returns version from resolved path", (t) => {
t.is(result.minor, 4);
}
t.is(statSync.callCount, 1);
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
t.is(lstatSync.callCount, 1);
t.deepEqual(lstatSync.args[0], [
XCODE_SELECT_LINK_PATH,
{ throwIfNoEntry: false },
]);
t.is(readlinkSync.callCount, 1);
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
});

View File

@@ -76,8 +76,16 @@ export function xcodeVersion(logger: Logger): semver.SemVer | undefined {
// Stat the expected symbolic link to check that it exists and is a symbolic link.
// The `readlinkSync` call below returns an empty string in either case and so
// this check allows us to distinguish between the two cases.
const stats = fs.statSync(XCODE_SELECT_LINK_PATH);
const stats = fs.lstatSync(XCODE_SELECT_LINK_PATH, {
throwIfNoEntry: false,
});
if (stats === undefined) {
logger.debug(
`'${XCODE_SELECT_LINK_PATH}' does not exist. Xcode is probably not installed.`,
);
return undefined;
}
if (!stats.isSymbolicLink()) {
logger.warning(
`${XCODE_SELECT_LINK_PATH} exists, but is not a symbolic link.`,