From 4c74175d36db89d4f6f439a0a79fb4bd47fc9c80 Mon Sep 17 00:00:00 2001 From: mahabaleshwars <147705296+mahabaleshwars@users.noreply.github.com> Date: Mon, 21 Sep 2026 15:11:37 +0530 Subject: [PATCH 1/2] path.resolve for composite actions --- __tests__/main.test.ts | 37 ++++++++++++++++++++++++++++++++++++- dist/setup/index.js | 6 ++++-- docs/advanced-usage.md | 2 +- src/main.ts | 6 ++++-- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 99cc0c86a..254f322ac 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -177,6 +177,10 @@ describe('main tests', () => { ); }); + afterEach(() => { + jest.restoreAllMocks(); + }); + each` contents | expected ${'12'} | ${'12'} @@ -315,11 +319,42 @@ describe('main tests', () => { // Assert expect(getNodeVersionFromFileSpy).toHaveBeenCalled(); + // A relative input is still resolved against the workspace. + expect(getNodeVersionFromFileSpy).toHaveBeenCalledWith( + path.join(process.env['GITHUB_WORKSPACE']!, '.nvmrc') + ); expect(infoSpy).toHaveBeenCalledWith( - `Resolved ${inputs['node-version-file']} as ${expectedVersionSpec}` + `Resolved ${path.join( + process.env['GITHUB_WORKSPACE']!, + '.nvmrc' + )} as ${expectedVersionSpec}` ); }, 10000); + it('reads node-version-file given as an absolute path outside the workspace', async () => { + // Arrange: a composite action passes `${{ github.action_path }}/.nvmrc`, + // which is absolute and may sit outside GITHUB_WORKSPACE. + // The workspace deliberately points at a different existing directory, + // so the absolute version file lies outside it. + const workspace = path.join(__dirname, 'mock'); + process.env['GITHUB_WORKSPACE'] = workspace; + const versionFilePath = path.join(__dirname, 'data', '.nvmrc'); + // Guard the premise: the version file lives outside the workspace. + expect(path.relative(workspace, versionFilePath).startsWith('..')).toBe( + true + ); + inputs['node-version-file'] = versionFilePath; + + // Act + await main.run(); + + // Assert: the path is used as provided and the real file is read. + // The expected `24` comes from `__tests__/data/.nvmrc` (`v24`). + expect(getNodeVersionFromFileSpy).toHaveBeenCalledWith(versionFilePath); + expect(infoSpy).toHaveBeenCalledWith(`Resolved ${versionFilePath} as 24`); + expect(core.setFailed as jest.Mock).not.toHaveBeenCalled(); + }, 10000); + it('should throw an error if node-version-file is not accessible', async () => { // Arrange inputs['node-version-file'] = 'non-existing-file'; diff --git a/dist/setup/index.js b/dist/setup/index.js index 95eeaae81..e055cfd54 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -101351,7 +101351,9 @@ function resolveVersionInput() { return version; } if (versionFileInput) { - const versionFilePath = external_path_.join(process.env.GITHUB_WORKSPACE, versionFileInput); + // `path.resolve` (unlike `path.join`) keeps an already-absolute input as-is, + // so a composite action can pass `${{ github.action_path }}/.nvmrc`. + const versionFilePath = external_path_.resolve(process.env.GITHUB_WORKSPACE, versionFileInput); const parsedVersion = getNodeVersionFromFile(versionFilePath); if (parsedVersion) { version = parsedVersion; @@ -101359,7 +101361,7 @@ function resolveVersionInput() { else { warning(`Could not determine node version from ${versionFilePath}. Falling back`); } - core_info(`Resolved ${versionFileInput} as ${version}`); + core_info(`Resolved ${versionFilePath} as ${version}`); } return version; } diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index f900317a9..e5fd682ce 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -79,7 +79,7 @@ steps: The `node-version-file` input accepts a path to a file containing the version of Node.js to be used by a project, for example `.nvmrc`, `.node-version`, `.tool-versions`, `mise.toml`, or `package.json`. If both the `node-version` and the `node-version-file` inputs are provided then the `node-version` input is used. See [supported version syntax](https://github.com/actions/setup-node#supported-version-syntax). -> The action will search for the node version file relative to the repository root. +> The action resolves a relative `node-version-file` path against `GITHUB_WORKSPACE`, which is the repository root by default. If the input is an absolute path, the action uses that path directly instead of appending it to the workspace. ```yaml steps: diff --git a/src/main.ts b/src/main.ts index 73fa75809..c545a4c9a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -120,7 +120,9 @@ function resolveVersionInput(): string { } if (versionFileInput) { - const versionFilePath = path.join( + // `path.resolve` (unlike `path.join`) keeps an already-absolute input as-is, + // so a composite action can pass `${{ github.action_path }}/.nvmrc`. + const versionFilePath = path.resolve( process.env.GITHUB_WORKSPACE!, versionFileInput ); @@ -135,7 +137,7 @@ function resolveVersionInput(): string { ); } - core.info(`Resolved ${versionFileInput} as ${version}`); + core.info(`Resolved ${versionFilePath} as ${version}`); } return version; From 0d52060a7729c343c5f750e774dfb18705cd9242 Mon Sep 17 00:00:00 2001 From: mahabaleshwars <147705296+mahabaleshwars@users.noreply.github.com> Date: Mon, 21 Sep 2026 15:33:24 +0530 Subject: [PATCH 2/2] log and documentation update --- __tests__/main.test.ts | 5 +---- dist/setup/index.js | 2 +- docs/advanced-usage.md | 2 +- src/main.ts | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 254f322ac..a49999353 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -324,10 +324,7 @@ describe('main tests', () => { path.join(process.env['GITHUB_WORKSPACE']!, '.nvmrc') ); expect(infoSpy).toHaveBeenCalledWith( - `Resolved ${path.join( - process.env['GITHUB_WORKSPACE']!, - '.nvmrc' - )} as ${expectedVersionSpec}` + `Resolved ${inputs['node-version-file']} as ${expectedVersionSpec}` ); }, 10000); diff --git a/dist/setup/index.js b/dist/setup/index.js index e055cfd54..d68238116 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -101361,7 +101361,7 @@ function resolveVersionInput() { else { warning(`Could not determine node version from ${versionFilePath}. Falling back`); } - core_info(`Resolved ${versionFilePath} as ${version}`); + core_info(`Resolved ${versionFileInput} as ${version}`); } return version; } diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index e5fd682ce..03e2b0248 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -79,7 +79,7 @@ steps: The `node-version-file` input accepts a path to a file containing the version of Node.js to be used by a project, for example `.nvmrc`, `.node-version`, `.tool-versions`, `mise.toml`, or `package.json`. If both the `node-version` and the `node-version-file` inputs are provided then the `node-version` input is used. See [supported version syntax](https://github.com/actions/setup-node#supported-version-syntax). -> The action resolves a relative `node-version-file` path against `GITHUB_WORKSPACE`, which is the repository root by default. If the input is an absolute path, the action uses that path directly instead of appending it to the workspace. +> The action resolves a relative `node-version-file` path against `GITHUB_WORKSPACE`, which is the repository root by default. If the input is a full absolute path, the action uses that path directly instead of appending it to the workspace. ```yaml steps: diff --git a/src/main.ts b/src/main.ts index c545a4c9a..6f09b27e6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -137,7 +137,7 @@ function resolveVersionInput(): string { ); } - core.info(`Resolved ${versionFilePath} as ${version}`); + core.info(`Resolved ${versionFileInput} as ${version}`); } return version;