diff --git a/README.md b/README.md index 873afe4..c27a954 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ Have a look under [Advanced Configuration](#advanced-configuration) for detailed - `python-version`: The Python version that was set. - `python-runtime-id`: An opaque identifier for the activated venv's Python runtime, including implementation, full version, and free-threaded build information. Useful as a cache-key component. - Empty when `activate-environment` is false or the runtime cannot be determined. + Empty when `activate-environment` is false. The action fails if the activated runtime cannot be determined. - `python-cache-hit`: A boolean value to indicate the Python cache entry was found. ### Python version diff --git a/__tests__/utils/python-runtime.test.ts b/__tests__/utils/python-runtime.test.ts index 328a73f..3e882fd 100644 --- a/__tests__/utils/python-runtime.test.ts +++ b/__tests__/utils/python-runtime.test.ts @@ -7,14 +7,12 @@ const mockExecFile = jest.fn< (...args: unknown[]) => Promise<{ stdout: string; stderr: string }> >(); -const mockDebug = jest.fn(); const originalPlatform = process.platform; const inputs = createSetupInputs({ activateEnvironment: true, pythonVersion: "3.15t", }); -jest.unstable_mockModule("@actions/core", () => ({ debug: mockDebug })); jest.unstable_mockModule("node:child_process", () => ({ // execFile's custom promisifier returns both stdout and stderr. execFile: Object.assign(mockExecFile, { [promisify.custom]: mockExecFile }), @@ -70,16 +68,13 @@ it.each([ ["pypy", [7, 3, 24, "beta", 2], "pypy-7.3.24b2"], ["pypy", [7, 3, 24, "candidate", 3], "pypy-7.3.24rc3"], ["graalpy", [25, 0, 0, "final", 0], "graalpy-25.0.0"], - ["pypy", [7, 3, 24, "unknown", 0], ""], ])("formats %s implementation version %j", async (name, version, expected) => { mockRuntime({ implementation: name, implementationVersion: version, pythonVersion: "3.11.15", }); - expect(await getPythonRuntimeId(inputs)).toBe( - expected ? `${expected}-python-3.11.15` : "", - ); + expect(await getPythonRuntimeId(inputs)).toBe(`${expected}-python-3.11.15`); }); it.each([ @@ -97,15 +92,25 @@ it.each([ ); }); -it.each([new Error("interpreter missing"), "", "not JSON", "null", "{}"])( - "returns an empty ID for interpreter failure: %s", - async (result) => { - if (result instanceof Error) { - mockExecFile.mockRejectedValue(result); - } else { - mockExecFile.mockResolvedValue({ stderr: "", stdout: result }); - } - expect(await getPythonRuntimeId(inputs)).toBe(""); - expect(mockDebug).toHaveBeenCalled(); - }, -); +it.each([ + new Error("interpreter missing"), + "", + "not JSON", + "null", + "{}", + JSON.stringify({ + freethreaded: false, + implementation: "pypy", + implementationVersion: [7, 3, 24, "unknown", 0], + pythonVersion: "3.11.15", + }), +])("rejects interpreter failure or invalid metadata: %s", async (result) => { + if (result instanceof Error) { + mockExecFile.mockRejectedValue(result); + } else { + mockExecFile.mockResolvedValue({ stderr: "", stdout: result }); + } + await expect(getPythonRuntimeId(inputs)).rejects.toThrow( + "Failed to identify the activated environment's Python runtime:", + ); +}); diff --git a/action.yml b/action.yml index 905ddd5..22729dc 100644 --- a/action.yml +++ b/action.yml @@ -108,7 +108,7 @@ outputs: python-version: description: "The Python version that was set." python-runtime-id: - description: "An opaque identifier for the activated venv's Python runtime, including implementation, full version, and free-threaded build information. Empty when activate-environment is false or the runtime cannot be determined." + description: "An opaque identifier for the activated venv's Python runtime, including implementation, full version, and free-threaded build information. Empty when activate-environment is false. The action fails if the activated runtime cannot be determined." python-cache-hit: description: "A boolean value to indicate the Python cache entry was found" runs: diff --git a/dist/setup/index.cjs b/dist/setup/index.cjs index 9c633ce..73b6d57 100644 --- a/dist/setup/index.cjs +++ b/dist/setup/index.cjs @@ -102116,10 +102116,10 @@ async function getPythonRuntimeId(inputs) { ); return formatRuntimeId(JSON.parse(stdout)); } catch (error2) { - debug( - `Failed to identify the activated environment's Python runtime. Error: ${error2 instanceof Error ? error2.message : String(error2)}` + throw new Error( + `Failed to identify the activated environment's Python runtime: ${error2 instanceof Error ? error2.message : String(error2)}`, + { cause: error2 } ); - return ""; } } diff --git a/docs/environment-and-tools.md b/docs/environment-and-tools.md index 9fdb139..da9c112 100644 --- a/docs/environment-and-tools.md +++ b/docs/environment-and-tools.md @@ -30,9 +30,9 @@ key: build-${{ runner.os }}-${{ runner.arch }}-${{ steps.setup-uv.outputs.python ``` The free-threaded marker describes the interpreter's build even when the GIL is -enabled at runtime. The output is empty when `activate-environment` is false or the -runtime cannot be determined. The existing `python-version` output and setup-uv's -cache keys are unaffected. +enabled at runtime. The output is empty when `activate-environment` is false. If +the activated runtime cannot be determined, the action fails. The existing +`python-version` output and setup-uv's cache keys are unaffected. You can customize the venv location with `venv-path`, for example to place it in the runner temp directory: diff --git a/src/utils/python-runtime.ts b/src/utils/python-runtime.ts index 0b5651c..c10153e 100644 --- a/src/utils/python-runtime.ts +++ b/src/utils/python-runtime.ts @@ -1,7 +1,6 @@ import { execFile } from "node:child_process"; import { join } from "node:path"; import { promisify } from "node:util"; -import * as core from "@actions/core"; import type { SetupInputs } from "./inputs"; const execFileAsync = promisify(execFile); @@ -79,9 +78,9 @@ export async function getPythonRuntimeId(inputs: SetupInputs): Promise { ); return formatRuntimeId(JSON.parse(stdout)); } catch (error) { - core.debug( - `Failed to identify the activated environment's Python runtime. Error: ${error instanceof Error ? error.message : String(error)}`, + throw new Error( + `Failed to identify the activated environment's Python runtime: ${error instanceof Error ? error.message : String(error)}`, + { cause: error }, ); - return ""; } }