Support absolute paths in node-version-file (#1633)

* path.resolve for composite actions

* log and documentation update

---------

Co-authored-by: mahabaleshwars <147705296+mahabaleshwars@users.noreply.github.com>
This commit is contained in:
v-mahabaleshwars
2026-09-22 01:55:01 +05:30
committed by GitHub
parent db0fb3a98e
commit 680d1e489b
4 changed files with 39 additions and 3 deletions
+32
View File
@@ -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';
+3 -1
View File
@@ -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;
+1 -1
View File
@@ -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:
+3 -1
View File
@@ -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
);