mirror of
https://github.com/astral-sh/ruff-action.git
synced 2026-05-23 15:20:45 +00:00
search in parent dir (#306)
Fixes: #164 --------- Co-authored-by: Clawdbot <clawdbot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
1d756c4b80
commit
5eee2a4332
@@ -406,6 +406,25 @@ jobs:
|
|||||||
src: >-
|
src: >-
|
||||||
__tests__/fixtures/python-project/src/python_project/__init__.py
|
__tests__/fixtures/python-project/src/python_project/__init__.py
|
||||||
__tests__/fixtures/python-project/src/python_project/hello_world.py
|
__tests__/fixtures/python-project/src/python_project/hello_world.py
|
||||||
|
test-parent-directory-pyproject:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Use version from parent directory pyproject.toml
|
||||||
|
id: ruff-action
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
src: __tests__/fixtures/parent-config-project/subproject
|
||||||
|
- name: Correct version gets installed
|
||||||
|
run: |
|
||||||
|
if [ "$RUFF_VERSION" != "0.10.0" ]; then
|
||||||
|
echo "Expected version 0.10.0 but got $RUFF_VERSION"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }}
|
||||||
|
|
||||||
all-tests-passed:
|
all-tests-passed:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -431,6 +450,7 @@ jobs:
|
|||||||
- test-args
|
- test-args
|
||||||
- test-failure
|
- test-failure
|
||||||
- test-multiple-src
|
- test-multiple-src
|
||||||
|
- test-parent-directory-pyproject
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- name: All tests passed
|
- name: All tests passed
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[project]
|
||||||
|
name = "parent-config-project"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Test fixture for parent directory pyproject.toml search"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"ruff==0.10.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""Hello world example."""
|
||||||
|
|
||||||
|
print("Hello, world!")
|
||||||
+108
-4
@@ -32146,7 +32146,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
const fs = __importStar(__nccwpck_require__(3024));
|
|
||||||
const path = __importStar(__nccwpck_require__(6760));
|
const path = __importStar(__nccwpck_require__(6760));
|
||||||
const core = __importStar(__nccwpck_require__(7484));
|
const core = __importStar(__nccwpck_require__(7484));
|
||||||
const exec = __importStar(__nccwpck_require__(5236));
|
const exec = __importStar(__nccwpck_require__(5236));
|
||||||
@@ -32155,6 +32154,7 @@ const download_version_1 = __nccwpck_require__(8255);
|
|||||||
const inputs_1 = __nccwpck_require__(9612);
|
const inputs_1 = __nccwpck_require__(9612);
|
||||||
const platforms_1 = __nccwpck_require__(8361);
|
const platforms_1 = __nccwpck_require__(8361);
|
||||||
const pyproject_1 = __nccwpck_require__(3929);
|
const pyproject_1 = __nccwpck_require__(3929);
|
||||||
|
const pyproject_finder_1 = __nccwpck_require__(6346);
|
||||||
async function run() {
|
async function run() {
|
||||||
const platform = (0, platforms_1.getPlatform)();
|
const platform = (0, platforms_1.getPlatform)();
|
||||||
const arch = (0, platforms_1.getArch)();
|
const arch = (0, platforms_1.getArch)();
|
||||||
@@ -32211,9 +32211,9 @@ async function determineVersion() {
|
|||||||
}
|
}
|
||||||
return await (0, download_version_1.resolveVersion)(versionFromPyproject || "latest", inputs_1.githubToken);
|
return await (0, download_version_1.resolveVersion)(versionFromPyproject || "latest", inputs_1.githubToken);
|
||||||
}
|
}
|
||||||
const pyProjectPath = path.join(inputs_1.src, "pyproject.toml");
|
const pyProjectPath = (0, pyproject_finder_1.findPyprojectToml)(inputs_1.src, process.env.GITHUB_WORKSPACE || ".");
|
||||||
if (!fs.existsSync(pyProjectPath)) {
|
if (!pyProjectPath) {
|
||||||
core.info(`Could not find ${pyProjectPath}. Using latest version.`);
|
core.info(`Could not find pyproject.toml. Using latest version.`);
|
||||||
return await (0, download_version_1.resolveVersion)("latest", inputs_1.githubToken);
|
return await (0, download_version_1.resolveVersion)("latest", inputs_1.githubToken);
|
||||||
}
|
}
|
||||||
const versionFromPyproject = (0, pyproject_1.getRuffVersionFromRequirementsFile)(pyProjectPath);
|
const versionFromPyproject = (0, pyproject_1.getRuffVersionFromRequirementsFile)(pyProjectPath);
|
||||||
@@ -32341,6 +32341,110 @@ function getPlatform() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 6346:
|
||||||
|
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||||
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||||
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||||
|
}
|
||||||
|
Object.defineProperty(o, k2, desc);
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || (function () {
|
||||||
|
var ownKeys = function(o) {
|
||||||
|
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||||
|
var ar = [];
|
||||||
|
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
return ownKeys(o);
|
||||||
|
};
|
||||||
|
return function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
|
exports.findPyprojectToml = findPyprojectToml;
|
||||||
|
const fs = __importStar(__nccwpck_require__(3024));
|
||||||
|
const path = __importStar(__nccwpck_require__(6760));
|
||||||
|
const core = __importStar(__nccwpck_require__(7484));
|
||||||
|
/**
|
||||||
|
* Search for a pyproject.toml file starting from the given directory
|
||||||
|
* and traversing upwards through parent directories until reaching
|
||||||
|
* the GitHub workspace root.
|
||||||
|
*
|
||||||
|
* @param startDir The directory to start the search from (e.g., the src input)
|
||||||
|
* @param workspaceRoot The GitHub workspace directory (GITHUB_WORKSPACE)
|
||||||
|
* @returns The path to the found pyproject.toml, or undefined if not found
|
||||||
|
*/
|
||||||
|
function findPyprojectToml(startDir, workspaceRoot) {
|
||||||
|
let currentDir = path.resolve(startDir);
|
||||||
|
const resolvedWorkspaceRoot = path.resolve(workspaceRoot);
|
||||||
|
while (true) {
|
||||||
|
const pyprojectPath = path.join(currentDir, "pyproject.toml");
|
||||||
|
core.debug(`Checking for ${pyprojectPath}`);
|
||||||
|
if (fs.existsSync(pyprojectPath)) {
|
||||||
|
core.info(`Found pyproject.toml at ${pyprojectPath}`);
|
||||||
|
return pyprojectPath;
|
||||||
|
}
|
||||||
|
// Check if we've reached the workspace root
|
||||||
|
if (currentDir === resolvedWorkspaceRoot) {
|
||||||
|
// If we're at workspace root and didn't find it, stop searching
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Move up to parent directory
|
||||||
|
const parentDir = path.dirname(currentDir);
|
||||||
|
// If parent is the same as current, we've reached the filesystem root
|
||||||
|
if (parentDir === currentDir) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentDir = parentDir;
|
||||||
|
// If we've gone past the workspace root, stop searching
|
||||||
|
if (isPathWithinWorkspace(currentDir, resolvedWorkspaceRoot) === false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check if a given path is within or equal to the workspace root.
|
||||||
|
*
|
||||||
|
* @param checkPath The path to check
|
||||||
|
* @param workspaceRoot The workspace root directory
|
||||||
|
* @returns true if within or equal to workspace, false if outside, undefined if can't determine
|
||||||
|
*/
|
||||||
|
function isPathWithinWorkspace(checkPath, workspaceRoot) {
|
||||||
|
try {
|
||||||
|
const checkPathResolved = path.resolve(checkPath);
|
||||||
|
const workspaceRootResolved = path.resolve(workspaceRoot);
|
||||||
|
// Check if checkPath starts with workspaceRoot (case-insensitive on Windows)
|
||||||
|
const relativePath = path.relative(workspaceRootResolved, checkPathResolved);
|
||||||
|
return !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 3929:
|
/***/ 3929:
|
||||||
|
|||||||
+7
-4
@@ -1,4 +1,3 @@
|
|||||||
import * as fs from "node:fs";
|
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import * as exec from "@actions/exec";
|
import * as exec from "@actions/exec";
|
||||||
@@ -23,6 +22,7 @@ import {
|
|||||||
type Platform,
|
type Platform,
|
||||||
} from "./utils/platforms";
|
} from "./utils/platforms";
|
||||||
import { getRuffVersionFromRequirementsFile } from "./utils/pyproject";
|
import { getRuffVersionFromRequirementsFile } from "./utils/pyproject";
|
||||||
|
import { findPyprojectToml } from "./utils/pyproject-finder";
|
||||||
|
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
const platform = getPlatform();
|
const platform = getPlatform();
|
||||||
@@ -107,9 +107,12 @@ async function determineVersion(): Promise<string> {
|
|||||||
}
|
}
|
||||||
return await resolveVersion(versionFromPyproject || "latest", githubToken);
|
return await resolveVersion(versionFromPyproject || "latest", githubToken);
|
||||||
}
|
}
|
||||||
const pyProjectPath = path.join(src, "pyproject.toml");
|
const pyProjectPath = findPyprojectToml(
|
||||||
if (!fs.existsSync(pyProjectPath)) {
|
src,
|
||||||
core.info(`Could not find ${pyProjectPath}. Using latest version.`);
|
process.env.GITHUB_WORKSPACE || ".",
|
||||||
|
);
|
||||||
|
if (!pyProjectPath) {
|
||||||
|
core.info(`Could not find pyproject.toml. Using latest version.`);
|
||||||
return await resolveVersion("latest", githubToken);
|
return await resolveVersion("latest", githubToken);
|
||||||
}
|
}
|
||||||
const versionFromPyproject =
|
const versionFromPyproject =
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
import * as path from "node:path";
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
import { findPyprojectToml } from "./pyproject-finder";
|
||||||
|
|
||||||
|
jest.mock("@actions/core", () => ({
|
||||||
|
debug: jest.fn(),
|
||||||
|
info: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("findPyprojectToml", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when pyproject.toml exists in src directory", () => {
|
||||||
|
it("should return the exact path", () => {
|
||||||
|
const fixturesDir = path.join(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"__tests__",
|
||||||
|
"fixtures",
|
||||||
|
);
|
||||||
|
const workspaceRoot = path.join(__dirname, "..", "..");
|
||||||
|
|
||||||
|
const result = findPyprojectToml(fixturesDir, workspaceRoot);
|
||||||
|
|
||||||
|
expect(result).toContain("pyproject.toml");
|
||||||
|
expect(result).toContain("fixtures");
|
||||||
|
expect(core.info).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when pyproject.toml exists only in parent directory", () => {
|
||||||
|
it("should search upwards and find the parent's pyproject.toml", () => {
|
||||||
|
// subproject doesn't have a pyproject.toml, but its parent (parent-config-project) does
|
||||||
|
const subprojectDir = path.join(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"__tests__",
|
||||||
|
"fixtures",
|
||||||
|
"parent-config-project",
|
||||||
|
"subproject",
|
||||||
|
);
|
||||||
|
const workspaceRoot = path.join(__dirname, "..", "..");
|
||||||
|
|
||||||
|
const result = findPyprojectToml(subprojectDir, workspaceRoot);
|
||||||
|
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(result).toContain("pyproject.toml");
|
||||||
|
expect(result).toContain("parent-config-project");
|
||||||
|
expect(core.info).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("boundary conditions", () => {
|
||||||
|
it("should stop searching at workspace root and return undefined when not found", () => {
|
||||||
|
// Create a path that won't have pyproject.toml above it
|
||||||
|
const nodeModulesDir = path.join(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"node_modules",
|
||||||
|
"@actions",
|
||||||
|
);
|
||||||
|
const workspaceRoot = path.join(__dirname, "..", "..");
|
||||||
|
|
||||||
|
const result = findPyprojectToml(nodeModulesDir, workspaceRoot);
|
||||||
|
|
||||||
|
// Should return undefined since there's no pyproject.toml in the search path
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
expect(core.info).not.toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining("Found pyproject.toml"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should find pyproject.toml when it exists at workspace root", () => {
|
||||||
|
// Use parent-config-project as the "workspace root" for this test
|
||||||
|
// Start from subproject (which has no pyproject.toml) to search up to workspace root
|
||||||
|
const subprojectDir = path.join(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"__tests__",
|
||||||
|
"fixtures",
|
||||||
|
"parent-config-project",
|
||||||
|
"subproject",
|
||||||
|
);
|
||||||
|
const workspaceRoot = path.join(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"__tests__",
|
||||||
|
"fixtures",
|
||||||
|
"parent-config-project",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = findPyprojectToml(subprojectDir, workspaceRoot);
|
||||||
|
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(result).toContain("pyproject.toml");
|
||||||
|
expect(result).toContain("parent-config-project");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should stop at workspace root even if searching from it", () => {
|
||||||
|
const workspaceRoot = path.join(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"__tests__",
|
||||||
|
"fixtures",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = findPyprojectToml(workspaceRoot, workspaceRoot);
|
||||||
|
|
||||||
|
// Should find pyproject.toml at workspace root
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(result).toContain("pyproject.toml");
|
||||||
|
expect(result).toContain("fixtures");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("edge cases", () => {
|
||||||
|
it("should handle relative paths", () => {
|
||||||
|
const srcDir = "./__tests__/fixtures";
|
||||||
|
const workspaceRoot = ".";
|
||||||
|
|
||||||
|
const result = findPyprojectToml(srcDir, workspaceRoot);
|
||||||
|
|
||||||
|
// Should work with relative paths
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(result).toContain("pyproject.toml");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle when src equals workspace root", () => {
|
||||||
|
const workspaceRoot = path.join(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"__tests__",
|
||||||
|
"fixtures",
|
||||||
|
);
|
||||||
|
const result = findPyprojectToml(workspaceRoot, workspaceRoot);
|
||||||
|
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(result).toContain("pyproject.toml");
|
||||||
|
expect(result).toContain("fixtures");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should log debug messages for each checked path", () => {
|
||||||
|
const pythonProjectDir = path.join(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"__tests__",
|
||||||
|
"fixtures",
|
||||||
|
"python-project",
|
||||||
|
);
|
||||||
|
const workspaceRoot = path.join(__dirname, "..", "..");
|
||||||
|
|
||||||
|
findPyprojectToml(pythonProjectDir, workspaceRoot);
|
||||||
|
|
||||||
|
expect(core.debug).toHaveBeenCalled();
|
||||||
|
const debugCalls = (core.debug as jest.Mock).mock.calls;
|
||||||
|
expect(debugCalls.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
// First debug call should be for the starting directory
|
||||||
|
expect(debugCalls[0][0]).toContain("Checking for");
|
||||||
|
expect(debugCalls[0][0]).toContain("python-project");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle paths with trailing slashes", () => {
|
||||||
|
const fixturesDir = `${path.join(__dirname, "..", "..", "__tests__", "fixtures")}/`;
|
||||||
|
const workspaceRoot = path.join(__dirname, "..", "..");
|
||||||
|
|
||||||
|
const result = findPyprojectToml(fixturesDir, workspaceRoot);
|
||||||
|
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(result).toContain("pyproject.toml");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import * as fs from "node:fs";
|
||||||
|
import * as path from "node:path";
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for a pyproject.toml file starting from the given directory
|
||||||
|
* and traversing upwards through parent directories until reaching
|
||||||
|
* the GitHub workspace root.
|
||||||
|
*
|
||||||
|
* @param startDir The directory to start the search from (e.g., the src input)
|
||||||
|
* @param workspaceRoot The GitHub workspace directory (GITHUB_WORKSPACE)
|
||||||
|
* @returns The path to the found pyproject.toml, or undefined if not found
|
||||||
|
*/
|
||||||
|
export function findPyprojectToml(
|
||||||
|
startDir: string,
|
||||||
|
workspaceRoot: string,
|
||||||
|
): string | undefined {
|
||||||
|
let currentDir = path.resolve(startDir);
|
||||||
|
const resolvedWorkspaceRoot = path.resolve(workspaceRoot);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const pyprojectPath = path.join(currentDir, "pyproject.toml");
|
||||||
|
core.debug(`Checking for ${pyprojectPath}`);
|
||||||
|
|
||||||
|
if (fs.existsSync(pyprojectPath)) {
|
||||||
|
core.info(`Found pyproject.toml at ${pyprojectPath}`);
|
||||||
|
return pyprojectPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we've reached the workspace root
|
||||||
|
if (currentDir === resolvedWorkspaceRoot) {
|
||||||
|
// If we're at workspace root and didn't find it, stop searching
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move up to parent directory
|
||||||
|
const parentDir = path.dirname(currentDir);
|
||||||
|
|
||||||
|
// If parent is the same as current, we've reached the filesystem root
|
||||||
|
if (parentDir === currentDir) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDir = parentDir;
|
||||||
|
|
||||||
|
// If we've gone past the workspace root, stop searching
|
||||||
|
if (isPathWithinWorkspace(currentDir, resolvedWorkspaceRoot) === false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given path is within or equal to the workspace root.
|
||||||
|
*
|
||||||
|
* @param checkPath The path to check
|
||||||
|
* @param workspaceRoot The workspace root directory
|
||||||
|
* @returns true if within or equal to workspace, false if outside, undefined if can't determine
|
||||||
|
*/
|
||||||
|
function isPathWithinWorkspace(
|
||||||
|
checkPath: string,
|
||||||
|
workspaceRoot: string,
|
||||||
|
): boolean | undefined {
|
||||||
|
try {
|
||||||
|
const checkPathResolved = path.resolve(checkPath);
|
||||||
|
const workspaceRootResolved = path.resolve(workspaceRoot);
|
||||||
|
|
||||||
|
// Check if checkPath starts with workspaceRoot (case-insensitive on Windows)
|
||||||
|
const relativePath = path.relative(
|
||||||
|
workspaceRootResolved,
|
||||||
|
checkPathResolved,
|
||||||
|
);
|
||||||
|
return !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user