diff --git a/__test__/working-directory.test.ts b/__test__/working-directory.test.ts new file mode 100644 index 0000000..c11a241 --- /dev/null +++ b/__test__/working-directory.test.ts @@ -0,0 +1,43 @@ +import * as fs from 'fs' +import * as os from 'os' +import * as path from 'path' +import {validateWorkingDirectory} from '../src/working-directory' + +describe('validateWorkingDirectory', () => { + let tmpDir: string + + beforeEach(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'github-script-test-')) + }) + + afterEach(() => { + fs.rmSync(tmpDir, {recursive: true, force: true}) + }) + + test('returns resolved path for existing directory', () => { + const result = validateWorkingDirectory(tmpDir) + expect(result).toBe(tmpDir) + }) + + test('resolves relative paths', () => { + const result = validateWorkingDirectory(tmpDir) + expect(path.isAbsolute(result)).toBe(true) + }) + + test('throws for non-existent directory', () => { + const nonExistent = path.join(tmpDir, 'does-not-exist') + expect(() => validateWorkingDirectory(nonExistent)).toThrow( + /does not exist/ + ) + expect(() => validateWorkingDirectory(nonExistent)).toThrow(nonExistent) + }) + + test('throws for file path instead of directory', () => { + const filePath = path.join(tmpDir, 'file.txt') + fs.writeFileSync(filePath, 'content') + expect(() => validateWorkingDirectory(filePath)).toThrow( + /is not a directory/ + ) + expect(() => validateWorkingDirectory(filePath)).toThrow(filePath) + }) +}) diff --git a/dist/index.js b/dist/index.js index 8dd5bbc..1b1ce68 100644 --- a/dist/index.js +++ b/dist/index.js @@ -65029,6 +65029,27 @@ function parseNumberArray(listString) { // EXTERNAL MODULE: external "path" var external_path_ = __nccwpck_require__(1017); +;// CONCATENATED MODULE: ./src/working-directory.ts + + +/** + * Validates that the given directory exists and is accessible. + * @param workingDirectory - The directory path to validate + * @returns The resolved absolute path + * @throws Error if the directory does not exist or is not a directory + */ +function validateWorkingDirectory(workingDirectory) { + const resolved = external_path_.resolve(workingDirectory); + if (!external_fs_.existsSync(resolved)) { + throw new Error(`working-directory "${workingDirectory}" does not exist (resolved to "${resolved}")`); + } + const stat = external_fs_.statSync(resolved); + if (!stat.isDirectory()) { + throw new Error(`working-directory "${workingDirectory}" is not a directory (resolved to "${resolved}")`); + } + return resolved; +} + ;// CONCATENATED MODULE: ./src/wrap-require.ts const wrapRequire = new Proxy(require, { @@ -65065,6 +65086,7 @@ const wrapRequire = new Proxy(require, { + process.on('unhandledRejection', handleError); main().catch(handleError); async function main() { @@ -65094,8 +65116,9 @@ async function main() { const script = core.getInput('script', { required: true }); const workingDirectory = core.getInput('working-directory'); if (workingDirectory) { - core.info(`Changing working directory to ${workingDirectory}`); - process.chdir(workingDirectory); + const resolved = validateWorkingDirectory(workingDirectory); + core.info(`Changing working directory to ${resolved}`); + process.chdir(resolved); } // Wrap getOctokit so secondary clients inherit retry, logging, // orchestration ID, and the action's retries input. diff --git a/src/main.ts b/src/main.ts index ffc7c8b..88fd475 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,6 +10,7 @@ import {RequestRequestOptions} from '@octokit/types' import {callAsyncFunction} from './async-function' import {createConfiguredGetOctokit} from './create-configured-getoctokit' import {RetryOptions, getRetryOptions, parseNumberArray} from './retry-options' +import {validateWorkingDirectory} from './working-directory' import {wrapRequire} from './wrap-require' process.on('unhandledRejection', handleError) @@ -62,8 +63,9 @@ async function main(): Promise { const workingDirectory = core.getInput('working-directory') if (workingDirectory) { - core.info(`Changing working directory to ${workingDirectory}`) - process.chdir(workingDirectory) + const resolved = validateWorkingDirectory(workingDirectory) + core.info(`Changing working directory to ${resolved}`) + process.chdir(resolved) } // Wrap getOctokit so secondary clients inherit retry, logging, diff --git a/src/working-directory.ts b/src/working-directory.ts new file mode 100644 index 0000000..bfba54d --- /dev/null +++ b/src/working-directory.ts @@ -0,0 +1,27 @@ +import * as fs from 'fs' +import * as path from 'path' + +/** + * Validates that the given directory exists and is accessible. + * @param workingDirectory - The directory path to validate + * @returns The resolved absolute path + * @throws Error if the directory does not exist or is not a directory + */ +export function validateWorkingDirectory(workingDirectory: string): string { + const resolved = path.resolve(workingDirectory) + + if (!fs.existsSync(resolved)) { + throw new Error( + `working-directory "${workingDirectory}" does not exist (resolved to "${resolved}")` + ) + } + + const stat = fs.statSync(resolved) + if (!stat.isDirectory()) { + throw new Error( + `working-directory "${workingDirectory}" is not a directory (resolved to "${resolved}")` + ) + } + + return resolved +}