Migrate to ESMBundler and node 24 (#345)

This commit is contained in:
Kevin Stillhammer
2026-03-28 16:52:59 +01:00
committed by GitHub
parent f611dfc122
commit 0be154b683
22 changed files with 66801 additions and 77877 deletions
+9
View File
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"module": "nodenext",
"moduleResolution": "nodenext",
"target": "es2022",
"types": ["node"]
},
"include": ["check-all-tests-passed-needs.ts"]
}
+3 -2
View File
@@ -27,14 +27,15 @@ jobs:
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with: with:
node-version: "20" node-version-file: .nvmrc
cache: npm
- run: | - run: |
npm ci --ignore-scripts npm ci --ignore-scripts
- run: | - run: |
npm run all npm run all
- name: Check all jobs are in all-tests-passed.needs - name: Check all jobs are in all-tests-passed.needs
run: | run: |
tsc check-all-tests-passed-needs.ts tsc -p tsconfig.json
node check-all-tests-passed-needs.js node check-all-tests-passed-needs.js
working-directory: .github/scripts working-directory: .github/scripts
- name: Make sure no changes from linters are detected - name: Make sure no changes from linters are detected
+3 -2
View File
@@ -16,11 +16,12 @@ jobs:
persist-credentials: false persist-credentials: false
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with: with:
node-version: "20" node-version-file: .nvmrc
cache: npm
- name: Update known checksums - name: Update known checksums
id: update-known-checksums id: update-known-checksums
run: run:
node dist/update-known-checksums/index.js node dist/update-known-checksums/index.cjs
src/download/checksum/known-checksums.ts ${{ secrets.GITHUB_TOKEN }} src/download/checksum/known-checksums.ts ${{ secrets.GITHUB_TOKEN }}
- run: npm ci --ignore-scripts && npm run all - run: npm ci --ignore-scripts && npm run all
- name: Create Pull Request - name: Create Pull Request
+1
View File
@@ -0,0 +1 @@
24
+122
View File
@@ -0,0 +1,122 @@
import path from "node:path";
import { fileURLToPath } from "node:url";
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
const debug = jest.fn();
const info = jest.fn();
jest.unstable_mockModule("@actions/core", () => ({
debug,
info,
}));
const { findPyprojectToml } = await import("../../src/utils/pyproject-finder");
const testFilePath = fileURLToPath(import.meta.url);
const testDir = path.dirname(testFilePath);
const repoRoot = path.resolve(testDir, "..", "..");
const fixturesDir = path.join(repoRoot, "__tests__", "fixtures");
describe("findPyprojectToml", () => {
beforeEach(() => {
debug.mockReset();
info.mockReset();
});
describe("when pyproject.toml exists in src directory", () => {
it("should return the exact path", () => {
const result = findPyprojectToml(fixturesDir, repoRoot);
expect(result).toContain("pyproject.toml");
expect(result).toContain("fixtures");
expect(info).toHaveBeenCalled();
});
});
describe("when pyproject.toml exists only in parent directory", () => {
it("should search upwards and find the parent's pyproject.toml", () => {
const subprojectDir = path.join(
fixturesDir,
"parent-config-project",
"subproject",
);
const result = findPyprojectToml(subprojectDir, repoRoot);
expect(result).toBeTruthy();
expect(result).toContain("pyproject.toml");
expect(result).toContain("parent-config-project");
expect(info).toHaveBeenCalled();
});
});
describe("boundary conditions", () => {
it("should stop searching at workspace root and return undefined when not found", () => {
const nodeModulesDir = path.join(repoRoot, "node_modules", "@actions");
const result = findPyprojectToml(nodeModulesDir, repoRoot);
expect(result).toBeUndefined();
expect(info).not.toHaveBeenCalledWith(
expect.stringContaining("Found pyproject.toml"),
);
});
it("should find pyproject.toml when it exists at workspace root", () => {
const parentConfigProjectDir = path.join(
fixturesDir,
"parent-config-project",
);
const subprojectDir = path.join(parentConfigProjectDir, "subproject");
const result = findPyprojectToml(subprojectDir, parentConfigProjectDir);
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 result = findPyprojectToml(fixturesDir, fixturesDir);
expect(result).toBeTruthy();
expect(result).toContain("pyproject.toml");
expect(result).toContain("fixtures");
});
});
describe("edge cases", () => {
it("should handle relative paths", () => {
const result = findPyprojectToml("./__tests__/fixtures", ".");
expect(result).toBeTruthy();
expect(result).toContain("pyproject.toml");
});
it("should handle when src equals workspace root", () => {
const result = findPyprojectToml(fixturesDir, fixturesDir);
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(fixturesDir, "python-project");
findPyprojectToml(pythonProjectDir, repoRoot);
expect(debug).toHaveBeenCalled();
expect(debug.mock.calls.length).toBeGreaterThan(0);
expect(debug.mock.calls[0][0]).toContain("Checking for");
expect(debug.mock.calls[0][0]).toContain("python-project");
});
it("should handle paths with trailing slashes", () => {
const result = findPyprojectToml(`${fixturesDir}/`, repoRoot);
expect(result).toBeTruthy();
expect(result).toContain("pyproject.toml");
});
});
});
@@ -1,77 +1,82 @@
import * as core from "@actions/core"; import { beforeEach, describe, expect, it, jest } from "@jest/globals";
import { findRuffVersionInSpec } from "./pyproject";
jest.mock("@actions/core", () => ({ const info = jest.fn();
info: jest.fn(), const warning = jest.fn();
warning: jest.fn(),
jest.unstable_mockModule("@actions/core", () => ({
info,
warning,
})); }));
const { findRuffVersionInSpec } = await import("../../src/utils/pyproject");
describe("findRuffVersionInSpec", () => { describe("findRuffVersionInSpec", () => {
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); info.mockReset();
warning.mockReset();
}); });
describe("ruff dependency strings", () => { describe("ruff dependency strings", () => {
it("should extract version from 'ruff==0.9.3'", () => { it("should extract version from 'ruff==0.9.3'", () => {
const result = findRuffVersionInSpec("ruff==0.9.3"); const result = findRuffVersionInSpec("ruff==0.9.3");
expect(result).toBe("0.9.3"); expect(result).toBe("0.9.3");
expect(core.info).toHaveBeenCalledWith( expect(info).toHaveBeenCalledWith(
"Found ruff version in requirements file: 0.9.3", "Found ruff version in requirements file: 0.9.3",
); );
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should extract version from 'ruff>=0.14'", () => { it("should extract version from 'ruff>=0.14'", () => {
const result = findRuffVersionInSpec("ruff>=0.14"); const result = findRuffVersionInSpec("ruff>=0.14");
expect(result).toBe(">=0.14"); expect(result).toBe(">=0.14");
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should extract version from 'ruff ~=1.0.0'", () => { it("should extract version from 'ruff ~=1.0.0'", () => {
const result = findRuffVersionInSpec("ruff ~=1.0.0"); const result = findRuffVersionInSpec("ruff ~=1.0.0");
expect(result).toBe("~=1.0.0"); expect(result).toBe("~=1.0.0");
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should extract version from 'ruff>=0.14,<1.0'", () => { it("should extract version from 'ruff>=0.14,<1.0'", () => {
const result = findRuffVersionInSpec("ruff>=0.14,<1.0"); const result = findRuffVersionInSpec("ruff>=0.14,<1.0");
expect(result).toBe(">=0.14,<1.0"); expect(result).toBe(">=0.14,<1.0");
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should extract version from 'ruff>=0.14,<2.0,!=1.5.0'", () => { it("should extract version from 'ruff>=0.14,<2.0,!=1.5.0'", () => {
const result = findRuffVersionInSpec("ruff>=0.14,<2.0,!=1.5.0"); const result = findRuffVersionInSpec("ruff>=0.14,<2.0,!=1.5.0");
expect(result).toBe(">=0.14,<2.0,!=1.5.0"); expect(result).toBe(">=0.14,<2.0,!=1.5.0");
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should return undefined for non-ruff dependency 'another-dep 0.1.6'", () => { it("should return undefined for non-ruff dependency 'another-dep 0.1.6'", () => {
const result = findRuffVersionInSpec("another-dep 0.1.6"); const result = findRuffVersionInSpec("another-dep 0.1.6");
expect(result).toBeUndefined(); expect(result).toBeUndefined();
expect(core.info).not.toHaveBeenCalled(); expect(info).not.toHaveBeenCalled();
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should return undefined for non-ruff dependency 'another-dep==0.1.6'", () => { it("should return undefined for non-ruff dependency 'another-dep==0.1.6'", () => {
const result = findRuffVersionInSpec("another-dep==0.1.6"); const result = findRuffVersionInSpec("another-dep==0.1.6");
expect(result).toBeUndefined(); expect(result).toBeUndefined();
expect(core.info).not.toHaveBeenCalled(); expect(info).not.toHaveBeenCalled();
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should strip trailing backslash", () => { it("should strip trailing backslash", () => {
const result = findRuffVersionInSpec("ruff==0.9.3 \\"); const result = findRuffVersionInSpec("ruff==0.9.3 \\");
expect(result).toBe("0.9.3"); expect(result).toBe("0.9.3");
expect(core.info).toHaveBeenCalledWith( expect(info).toHaveBeenCalledWith(
"Found ruff version in requirements file: 0.9.3", "Found ruff version in requirements file: 0.9.3",
); );
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should strip trailing backslash with whitespace", () => { it("should strip trailing backslash with whitespace", () => {
const result = findRuffVersionInSpec(" ruff==0.9.3 \\ "); const result = findRuffVersionInSpec(" ruff==0.9.3 \\ ");
expect(result).toBe("0.9.3"); expect(result).toBe("0.9.3");
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
}); });
@@ -81,10 +86,10 @@ describe("findRuffVersionInSpec", () => {
'ruff>=0.14 ; python_version >= "3.11"', 'ruff>=0.14 ; python_version >= "3.11"',
); );
expect(result).toBe(">=0.14"); expect(result).toBe(">=0.14");
expect(core.info).toHaveBeenCalledWith( expect(info).toHaveBeenCalledWith(
"Found ruff version in requirements file: >=0.14", "Found ruff version in requirements file: >=0.14",
); );
expect(core.warning).toHaveBeenCalledWith( expect(warning).toHaveBeenCalledWith(
"Environment markers are ignored. ruff is a standalone tool that works independently of Python version.", "Environment markers are ignored. ruff is a standalone tool that works independently of Python version.",
); );
}); });
@@ -94,7 +99,7 @@ describe("findRuffVersionInSpec", () => {
"ruff==0.9.3 ; sys_platform == 'linux'", "ruff==0.9.3 ; sys_platform == 'linux'",
); );
expect(result).toBe("0.9.3"); expect(result).toBe("0.9.3");
expect(core.warning).toHaveBeenCalledWith( expect(warning).toHaveBeenCalledWith(
"Environment markers are ignored. ruff is a standalone tool that works independently of Python version.", "Environment markers are ignored. ruff is a standalone tool that works independently of Python version.",
); );
}); });
@@ -104,7 +109,7 @@ describe("findRuffVersionInSpec", () => {
'ruff>=0.14 ; python_version >= "3.11" and sys_platform == "linux"', 'ruff>=0.14 ; python_version >= "3.11" and sys_platform == "linux"',
); );
expect(result).toBe(">=0.14"); expect(result).toBe(">=0.14");
expect(core.warning).toHaveBeenCalledWith( expect(warning).toHaveBeenCalledWith(
"Environment markers are ignored. ruff is a standalone tool that works independently of Python version.", "Environment markers are ignored. ruff is a standalone tool that works independently of Python version.",
); );
}); });
@@ -114,7 +119,7 @@ describe("findRuffVersionInSpec", () => {
'ruff>=0.14,<1.0 ; python_version >= "3.11"', 'ruff>=0.14,<1.0 ; python_version >= "3.11"',
); );
expect(result).toBe(">=0.14,<1.0"); expect(result).toBe(">=0.14,<1.0");
expect(core.warning).toHaveBeenCalledWith( expect(warning).toHaveBeenCalledWith(
"Environment markers are ignored. ruff is a standalone tool that works independently of Python version.", "Environment markers are ignored. ruff is a standalone tool that works independently of Python version.",
); );
}); });
@@ -124,7 +129,7 @@ describe("findRuffVersionInSpec", () => {
it("should handle whitespace", () => { it("should handle whitespace", () => {
const result = findRuffVersionInSpec(" ruff >=0.14 "); const result = findRuffVersionInSpec(" ruff >=0.14 ");
expect(result).toBe(">=0.14"); expect(result).toBe(">=0.14");
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should handle whitespace with environment markers", () => { it("should handle whitespace with environment markers", () => {
@@ -132,28 +137,28 @@ describe("findRuffVersionInSpec", () => {
" ruff >=0.14 ; python_version >= '3.11' ", " ruff >=0.14 ; python_version >= '3.11' ",
); );
expect(result).toBe(">=0.14"); expect(result).toBe(">=0.14");
expect(core.warning).toHaveBeenCalled(); expect(warning).toHaveBeenCalled();
}); });
it("should return undefined for empty string", () => { it("should return undefined for empty string", () => {
const result = findRuffVersionInSpec(""); const result = findRuffVersionInSpec("");
expect(result).toBeUndefined(); expect(result).toBeUndefined();
expect(core.info).not.toHaveBeenCalled(); expect(info).not.toHaveBeenCalled();
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should return undefined for whitespace only", () => { it("should return undefined for whitespace only", () => {
const result = findRuffVersionInSpec(" "); const result = findRuffVersionInSpec(" ");
expect(result).toBeUndefined(); expect(result).toBeUndefined();
expect(core.info).not.toHaveBeenCalled(); expect(info).not.toHaveBeenCalled();
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should return undefined for just semicolon", () => { it("should return undefined for just semicolon", () => {
const result = findRuffVersionInSpec(";"); const result = findRuffVersionInSpec(";");
expect(result).toBeUndefined(); expect(result).toBeUndefined();
expect(core.info).not.toHaveBeenCalled(); expect(info).not.toHaveBeenCalled();
expect(core.warning).not.toHaveBeenCalled(); expect(warning).not.toHaveBeenCalled();
}); });
it("should handle exact example from issue #256", () => { it("should handle exact example from issue #256", () => {
@@ -161,10 +166,10 @@ describe("findRuffVersionInSpec", () => {
'ruff>=0.14 ; python_version >= "3.11"', 'ruff>=0.14 ; python_version >= "3.11"',
); );
expect(result).toBe(">=0.14"); expect(result).toBe(">=0.14");
expect(core.info).toHaveBeenCalledWith( expect(info).toHaveBeenCalledWith(
"Found ruff version in requirements file: >=0.14", "Found ruff version in requirements file: >=0.14",
); );
expect(core.warning).toHaveBeenCalledWith( expect(warning).toHaveBeenCalledWith(
"Environment markers are ignored. ruff is a standalone tool that works independently of Python version.", "Environment markers are ignored. ruff is a standalone tool that works independently of Python version.",
); );
}); });
@@ -174,7 +179,7 @@ describe("findRuffVersionInSpec", () => {
"ruff>=0.14 ; python_version >= '3.11'", "ruff>=0.14 ; python_version >= '3.11'",
); );
expect(result).toBe(">=0.14"); expect(result).toBe(">=0.14");
expect(core.warning).toHaveBeenCalled(); expect(warning).toHaveBeenCalled();
}); });
it("should handle double-quoted environment markers", () => { it("should handle double-quoted environment markers", () => {
@@ -182,7 +187,7 @@ describe("findRuffVersionInSpec", () => {
'ruff>=0.14 ; python_version >= "3.11"', 'ruff>=0.14 ; python_version >= "3.11"',
); );
expect(result).toBe(">=0.14"); expect(result).toBe(">=0.14");
expect(core.warning).toHaveBeenCalled(); expect(warning).toHaveBeenCalled();
}); });
}); });
}); });
+2 -2
View File
@@ -30,8 +30,8 @@ outputs:
ruff-version: ruff-version:
description: "The installed ruff version. Useful when using latest." description: "The installed ruff version. Useful when using latest."
runs: runs:
using: "node20" using: "node24"
main: "dist/ruff-action/index.js" main: "dist/ruff-action/index.cjs"
branding: branding:
icon: "code" icon: "code"
color: "black" color: "black"
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.3.2/schema.json", "$schema": "https://biomejs.dev/schemas/2.4.9/schema.json",
"assist": { "assist": {
"actions": { "actions": {
"source": { "source": {
Generated Vendored
+32322
View File
File diff suppressed because one or more lines are too long
Generated Vendored
-39550
View File
File diff suppressed because one or more lines are too long
+29510
View File
File diff suppressed because one or more lines are too long
-37282
View File
File diff suppressed because one or more lines are too long
-12
View File
@@ -1,12 +0,0 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
collectCoverageFrom: ["src/**/*.ts", "!src/**/*.d.ts"],
moduleFileExtensions: ["ts", "js"],
preset: "ts-jest",
roots: ["<rootDir>/src"],
testEnvironment: "node",
testMatch: ["**/*.test.ts"],
transform: {
"^.+\\.ts$": "ts-jest",
},
};
+12
View File
@@ -0,0 +1,12 @@
import { createDefaultEsmPreset } from "ts-jest";
const preset = createDefaultEsmPreset();
/** @type {import('jest').Config} */
export default {
...preset,
collectCoverageFrom: ["src/**/*.ts", "!src/**/*.d.ts"],
moduleFileExtensions: ["ts", "js", "mjs"],
testEnvironment: "node",
testMatch: ["<rootDir>/__tests__/**/*.test.ts"],
};
+4690 -745
View File
File diff suppressed because it is too large Load Diff
+18 -16
View File
@@ -2,16 +2,18 @@
"name": "ruff-action", "name": "ruff-action",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"type": "module",
"description": "A GitHub Action to run Ruff, an extremely fast Python linter and code formatter.", "description": "A GitHub Action to run Ruff, an extremely fast Python linter and code formatter.",
"main": "dist/ruff-action/index.js", "main": "dist/ruff-action/index.cjs",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc --noEmit",
"check": "biome check --write", "check": "biome check --write",
"package": "ncc build -o dist/ruff-action src/ruff-action.ts && ncc build -o dist/update-known-checksums src/update-known-checksums.ts", "package": "node scripts/build-dist.mjs",
"act": "act pull_request -W .github/workflows/test.yml --container-architecture linux/amd64 -s GITHUB_TOKEN=\"$(gh auth token)\"", "act": "act pull_request -W .github/workflows/test.yml --container-architecture linux/amd64 -s GITHUB_TOKEN=\"$(gh auth token)\"",
"update-known-checksums": "RUNNER_TEMP=known_checksums node dist/update-known-checksums/index.js src/download/checksum/known-checksums.ts \"$(gh auth token)\"", "update-known-checksums": "RUNNER_TEMP=known_checksums node dist/update-known-checksums/index.cjs src/download/checksum/known-checksums.ts \"$(gh auth token)\"",
"test": "jest", "test:unit": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
"all": "npm ci --ignore-scripts && npm run build && npm run check && npm run test && npm run package" "test": "npm run build && npm run test:unit",
"all": "npm run build && npm run check && npm run package && npm run test:unit"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -26,9 +28,9 @@
"author": "@eifinger", "author": "@eifinger",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^3.0.0",
"@actions/exec": "^1.1.1", "@actions/exec": "^3.0.0",
"@actions/tool-cache": "^2.0.2", "@actions/tool-cache": "^4.0.0",
"@octokit/core": "^7.0.3", "@octokit/core": "^7.0.3",
"@octokit/plugin-paginate-rest": "^13.1.1", "@octokit/plugin-paginate-rest": "^13.1.1",
"@octokit/plugin-rest-endpoint-methods": "^16.0.0", "@octokit/plugin-rest-endpoint-methods": "^16.0.0",
@@ -36,15 +38,15 @@
"smol-toml": "^1.6.0" "smol-toml": "^1.6.0"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.3.2", "@biomejs/biome": "^2.4.7",
"@types/jest": "^29.5.14", "@types/jest": "^29.5.14",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
"@types/node": "^24.10.9", "@types/node": "^25.5.0",
"@types/semver": "^7.7.1", "@types/semver": "^7.7.1",
"@vercel/ncc": "^0.38.3", "esbuild": "^0.27.4",
"jest": "^29.7.0", "jest": "^30.3.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.1",
"ts-jest": "^29.2.5", "ts-jest": "^29.4.6",
"typescript": "^5.9.2" "typescript": "^5.9.3"
} }
} }
+41
View File
@@ -0,0 +1,41 @@
import { mkdir, rm } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { build } from "esbuild";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const repoRoot = path.resolve(__dirname, "..");
const builds = [
{
entryPoint: path.join(repoRoot, "src", "ruff-action.ts"),
outfile: path.join(repoRoot, "dist", "ruff-action", "index.cjs"),
staleOutfile: path.join(repoRoot, "dist", "ruff-action", "index.js"),
},
{
entryPoint: path.join(repoRoot, "src", "update-known-checksums.ts"),
outfile: path.join(repoRoot, "dist", "update-known-checksums", "index.cjs"),
staleOutfile: path.join(
repoRoot,
"dist",
"update-known-checksums",
"index.js",
),
},
];
await Promise.all(
builds.map(async ({ entryPoint, outfile, staleOutfile }) => {
await rm(staleOutfile, { force: true });
await mkdir(path.dirname(outfile), { recursive: true });
await build({
bundle: true,
entryPoints: [entryPoint],
format: "cjs",
outfile,
platform: "node",
target: "node24",
});
}),
);
+7 -6
View File
@@ -136,16 +136,17 @@ function setOutputFormat() {
} }
function addMatchers(): void { function addMatchers(): void {
const matchersPath = path.join( const actionRoot = getActionRoot();
__dirname, const matchersPath = path.join(actionRoot, ".github", "matchers");
`..${path.sep}..`,
".github",
"matchers",
);
core.info(`##[add-matcher]${path.join(matchersPath, "check.json")}`); core.info(`##[add-matcher]${path.join(matchersPath, "check.json")}`);
core.info(`##[add-matcher]${path.join(matchersPath, "format.json")}`); core.info(`##[add-matcher]${path.join(matchersPath, "format.json")}`);
} }
function getActionRoot(): string {
const entrypoint = process.argv[1] ?? process.cwd();
return path.resolve(path.dirname(entrypoint), "..", "..");
}
async function runRuff( async function runRuff(
ruffExecutablePath: string, ruffExecutablePath: string,
args: string[], args: string[],
+2 -2
View File
@@ -10,9 +10,9 @@ const PaginatingOctokit = Octokit.plugin(paginateRest, restEndpointMethods);
async function run(): Promise<void> { async function run(): Promise<void> {
const checksumFilePath = process.argv.slice(2)[0]; const checksumFilePath = process.argv.slice(2)[0];
const github_token = process.argv.slice(2)[1]; const githubToken = process.argv.slice(2)[1];
const octokit = new PaginatingOctokit({ auth: github_token }); const octokit = new PaginatingOctokit({ auth: githubToken });
const response = await octokit.paginate(octokit.rest.repos.listReleases, { const response = await octokit.paginate(octokit.rest.repos.listReleases, {
owner: OWNER, owner: OWNER,
-183
View File
@@ -1,183 +0,0 @@
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");
});
});
});
+9 -30
View File
@@ -26,30 +26,20 @@ export function findPyprojectToml(
core.info(`Found pyproject.toml at ${pyprojectPath}`); core.info(`Found pyproject.toml at ${pyprojectPath}`);
return pyprojectPath; return pyprojectPath;
} }
// Check if we've reached the workspace root
if (currentDir === resolvedWorkspaceRoot) { if (currentDir === resolvedWorkspaceRoot) {
// If we're at workspace root and didn't find it, stop searching return undefined;
break;
} }
// Move up to parent directory
const parentDir = path.dirname(currentDir); const parentDir = path.dirname(currentDir);
if (
// If parent is the same as current, we've reached the filesystem root parentDir === currentDir ||
if (parentDir === currentDir) { !isPathWithinWorkspace(parentDir, resolvedWorkspaceRoot)
break; ) {
return undefined;
} }
currentDir = parentDir; currentDir = parentDir;
// If we've gone past the workspace root, stop searching
if (isPathWithinWorkspace(currentDir, resolvedWorkspaceRoot) === false) {
break;
}
} }
return undefined;
} }
/** /**
@@ -62,18 +52,7 @@ export function findPyprojectToml(
function isPathWithinWorkspace( function isPathWithinWorkspace(
checkPath: string, checkPath: string,
workspaceRoot: string, workspaceRoot: string,
): boolean | undefined { ): boolean {
try { const relativePath = path.relative(workspaceRoot, checkPath);
const checkPathResolved = path.resolve(checkPath); return !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
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;
}
} }
+8 -8
View File
@@ -1,12 +1,12 @@
{ {
"compilerOptions": { "compilerOptions": {
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, "esModuleInterop": true,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, "isolatedModules": true,
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, "module": "esnext",
"outDir": "./lib" /* Redirect output structure to the directory. */, "moduleResolution": "bundler",
"rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, "noImplicitAny": true,
"strict": true /* Enable all strict type-checking options. */, "strict": true,
"target": "ES2022" /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ "target": "ES2022"
}, },
"exclude": ["node_modules", "**/*.test.ts"] "include": ["src/**/*.ts"]
} }