From 2df360ee605d938e29b31f8c044f72619c2f38cc Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Thu, 2 May 2024 14:24:31 -0400 Subject: [PATCH 1/5] Disable linting rule --- .eslintrc.yml | 1 + src/bootstrap.js | 1 - src/command.ts | 2 -- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 897635d..d00cef2 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -34,6 +34,7 @@ extends: rules: { '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', 'camelcase': 'off', 'eslint-comments/no-use': 'off', 'i18n-text/no-en': 'off', diff --git a/src/bootstrap.js b/src/bootstrap.js index 4b2e77f..87f5334 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-var-requires */ /** diff --git a/src/command.ts b/src/command.ts index 0ab23d7..7176dcf 100644 --- a/src/command.ts +++ b/src/command.ts @@ -26,11 +26,9 @@ export async function makeProgram(): Promise { if (!fs.statSync(actionPath).isDirectory()) throw new InvalidArgumentError('Action path must be a directory') } catch (err: any) { - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ if ('code' in err && err.code === 'ENOENT') throw new InvalidArgumentError('Action path does not exist') else throw new InvalidArgumentError(err.message as string) - /* eslint-enable @typescript-eslint/no-unsafe-member-access */ } // Save the action path to environment metadata From 1795e2d26e11dc497e5ee20c9e99783e1af62ae8 Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Thu, 2 May 2024 14:25:16 -0400 Subject: [PATCH 2/5] Clarify dotenv file name --- CONTRIBUTING.md | 4 ++-- __tests__/command.test.ts | 2 +- src/command.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b188d6f..7030064 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,10 +56,10 @@ against various repositories with different configurations. 1. Test your updated version ```bash - local-action run + local-action run # Or... - npm exec local-action run + npm exec local-action run ``` Once you're finished testing, make sure to unlink! diff --git a/__tests__/command.test.ts b/__tests__/command.test.ts index c40ac27..6b5b1d3 100644 --- a/__tests__/command.test.ts +++ b/__tests__/command.test.ts @@ -194,7 +194,7 @@ describe('Commmand', () => { process_stderrSpy.mockRestore() }) - it('Throws if the env file does not exist', async () => { + it('Throws if the dotenv file does not exist', async () => { process_stderrSpy = jest .spyOn(process.stderr, 'write') .mockImplementation() diff --git a/src/command.ts b/src/command.ts index 7176dcf..d5b61c4 100644 --- a/src/command.ts +++ b/src/command.ts @@ -100,7 +100,7 @@ export async function makeProgram(): Promise { 'Action entrypoint (relative to the action directory)', checkEntrypoint ) - .argument('', 'Path to the local .env file', checkDotenvFile) + .argument('', 'Path to the local .env file', checkDotenvFile) .action(async () => { await runAction() }) From 697450d50569ed0ec8ec8da8d3f741bc7de30382 Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Thu, 2 May 2024 14:25:34 -0400 Subject: [PATCH 3/5] Add step to install dependencies --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 227d060..81b7f95 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,12 @@ For additional information about transpiled action code, see git clone https://github.com/github/local-action.git ``` +1. Install dependencies + + ```bash + npm ci + ``` + 1. Install via `npm` ```bash From 06438a950c4d86063ab03ef43956b7eb3bcc2fe7 Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Thu, 2 May 2024 14:26:03 -0400 Subject: [PATCH 4/5] Get version from package.json --- package-lock.json | 4 ++-- package.json | 2 +- src/command.ts | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1677302..cf839bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@github/local-action", - "version": "1.4.2", + "version": "1.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@github/local-action", - "version": "1.4.2", + "version": "1.5.0", "license": "MIT", "dependencies": { "@actions/core": "^1.10.1", diff --git a/package.json b/package.json index 54a5daa..d66ebcd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@github/local-action", "description": "Local Debugging for GitHub Actions", - "version": "1.4.2", + "version": "1.5.0", "author": "Nick Alteen ", "private": false, "homepage": "https://github.com/github/local-action", diff --git a/src/command.ts b/src/command.ts index d5b61c4..66601d7 100644 --- a/src/command.ts +++ b/src/command.ts @@ -89,7 +89,9 @@ export async function makeProgram(): Promise { program .name('local-action') .description('Test a GitHub Action locally') - .version('1.0.0') + .version( + `Version: ${JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'package.json'), 'utf-8')).version as string}` + ) program .command('run', { isDefault: true }) From fd8d9d65c29e0ef87373e8d07b5ef2e7fcc753e8 Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Thu, 2 May 2024 14:27:03 -0400 Subject: [PATCH 5/5] Format and add Windows path escape --- bin/local-action | 98 ++++++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/bin/local-action b/bin/local-action index 81fc708..131c96d 100755 --- a/bin/local-action +++ b/bin/local-action @@ -1,46 +1,72 @@ #!/usr/bin/env node +const fs = require('fs') const path = require('path') const { execSync } = require('child_process') -// Back up the environment -const envBackup = { ...process.env } -const pathBackup = process.env.PATH - /** * This script is used to run the local action. It sets the NODE_OPTIONS - * environment variable to require the bootstrap file, which sets up the + * environment variable to require the bootstrap script, which sets up the * TypeScript environment for the action. */ -// Set the first argument (path to action directory) as an environment variable. -// This is used in the bootstrap file to check for a `tsconfig.json`. -const actionPath = process.argv[2] ? path.resolve(process.argv[2]) : '' -process.env.TARGET_ACTION_PATH = actionPath - -// Get the other arguments, if present. Validation and error handling is done -// within the package itself. -const entrypoint = process.argv[3] ?? '' -const dotenvFile = process.argv[4] ? path.resolve(process.argv[4]) : '' - -// Get the absolute path to the `@github/local-action` package. -const packagePath = path.resolve(__dirname, '..') -const packageIndex = path.join(packagePath, 'src', 'index.ts') - -// Set the NODE_OPTIONS environment variable to require the bootstrap file -const options = `--require "${path.join(packagePath, 'src', 'bootstrap.js')}"` -process.env.NODE_OPTIONS = process.env.NODE_OPTIONS - ? `${process.env.NODE_OPTIONS} ${options}` - : options - -// Run the action -const command = `npx tsx "${packageIndex}" "${actionPath}" "${entrypoint}" "${dotenvFile}"` - -try { - execSync(command, { cwd: packagePath, stdio: 'inherit' }) -} catch (error) { - process.exit(error.status) -} finally { - // Restore the environment - process.env = { ...envBackup } - process.env.PATH = pathBackup +function entrypoint() { + // Save the current environment and path. + const envBackup = { ...process.env } + const pathBackup = process.env.PATH + + // Delete the TARGET_ACTION_PATH environment variable. + delete process.env.TARGET_ACTION_PATH + + try { + // Get the absolute path to the `@github/local-action` package. + const packagePath = path.resolve(__dirname, '..') + + // Get the absolute path to the bootstrap script. On Windows systems, this + // need to be double-escaped so the path resolves correctly. + const bootstrapPath = + process.platform === 'win32' + ? path.join(packagePath, 'src', 'bootstrap.js').replaceAll('\\', '\\\\') + : path.join(packagePath, 'src', 'bootstrap.js') + + // Require the bootstrap script in NODE_OPTIONS. + process.env.NODE_OPTIONS = process.env.NODE_OPTIONS + ? `${process.env.NODE_OPTIONS} --require ${bootstrapPath}` + : `--require ${bootstrapPath}` + + // Start building the command to run local-action. + let command = `npx tsx "${path.join(packagePath, 'src', 'index.ts')}"` + + // Process the input arguments. + if (process.argv.length === 2) { + // No arguments...display the help message. + command += ' --help' + } else { + // Iterate over the arguments and build the command. + for (const arg of process.argv.slice(2)) { + // If the argument is a directory and TARGET_ACTION_PATH is not set, set + // it to the absolute path of the directory. The first directory is the + // target action path. + if ( + !process.env.TARGET_ACTION_PATH && + fs.existsSync(path.resolve(arg)) && + fs.lstatSync(path.resolve(arg)).isDirectory() + ) + process.env.TARGET_ACTION_PATH = path.resolve(arg) + + // Append the argument to the command. + command += ` ${arg}` + } + } + + // Run the command. + execSync(command, { cwd: packagePath, stdio: 'inherit' }) + } catch (error) { + process.exit(error.status) + } finally { + // Restore the environment. + process.env = { ...envBackup } + process.env.PATH = pathBackup + } } + +entrypoint()