diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 99cc0c86..a4999935 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,39 @@ 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}` ); }, 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 95eeaae8..d6823811 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; diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index f900317a..03e2b024 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 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 73fa7580..6f09b27e 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 );